Search code examples
clanguage-lawyer

memset_s(): What does the standard mean with this piece of text?


In C11, K.3.7.4.1 The memset_s function, I found this bit of rather confusing text:

Unlike memset, any call to the memset_s function shall be evaluated strictly according to the rules of the abstract machine as described in (5.1.2.3). That is, any call to the memset_s function shall assume that the memory indicated by s and n may be accessible in the future and thus must contain the values indicated by c.

This implies that memset is not (necessarily) "evaluated strictly according to the rules of the abstract machine". (The chapter referenced is 5.1.2.3 Program execution.)

I fail to understand the leeway the standard gives to memset that is explicitly ruled out here for memset_s, and what that would mean for an implementor of either function.


Solution

  • Imagine you have read a password:

    {
        char password[128];
    
        if (fgets(password, sizeof(password), stdin) != 0)
        {
            password[strcspn(password, "\n\r")] = '\0';
            validate_password(password);
            memset(password, '\0', sizeof(password));
        }
    }
    

    You've carefully zapped the password so it can't be found accidentally.

    Unfortunately, the compiler is allowed to omit that memset() call because password is not used again. The rule for memset_s() means that the call cannot be omitted; the password variable must be zeroed, regardless of optimization.

    memset_s(password, sizeof(password), '\0', sizeof(password));
    

    This is one of the few really useful features in Annex K. (We can debate the merits of having to repeat the size. However, in a more general case, the second size can be a variable, not a constant, and then the first size becomes a runtime protection against the variable being out of control.)

    Note that this requirement is placed on the compiler rather than the library. The memset_s() function will behave correctly if it is called, just as memset() will behave correctly if it is called. The rule under discussion says that the compiler must call memset_s(), even though it may be able omit the call to memset() because the variable is never used again.