oss-sec mailing list archives

Re: How to do secure coding and create secure software


From: Dan Cross <crossd () gmail com>
Date: Mon, 29 Sep 2025 14:02:17 -0400

On Mon, Sep 29, 2025 at 12:49 PM Dan Cross <crossd () gmail com> wrote:
[snip]
For example, in your original email, you mention validating the length
of a 0-terminated C string. You suggest that one may use the `strnlen`
function to do this, since there's no guarantee that an input buffer
actually contains a 0 terminator (i.e., it may not be a string).
Further you say, "For example, you can specify that the minimum length
of a string argument should be 1 and the maximum length of the string
argument should be 1024." You then write, "The code will be 'len =
strnlen(str, 1025); if (len == 1025) { return error; }'".  Well, now
we have an ambiguity; C defines "the string" as containing the 0
terminating byte (cf eg C18, sec 7.1.1 para 1: "A string is a
contiguous sequence of characters terminated by and including the
first null character"). `strlen`, on the other hand, returns the
number of characters before the terminating zero, which is not the
same thing.  Did you really mean the maximum length of the string, or
did you mean its size?

Suppose that you truly meant that the maximum length, e.g., as would
be returned by `strlen`, should be 1024; then that string's _size_
could be up to 1025, as for a maximally sized string the terminating
null character would be the 1025'th char. But this code returns an
error on the value 1025; clearly this rejects a maximum length string.

Oops, and I'm off by one myself here: if b[1024]==0, then the return
value of `strnlen` is 1024, not 1025. But regardless, the subsequent
point stands: the function, as written, is ambiguous with respect to
length and size.

        - Dan C.

Reading between the lines, it appears what you mean is that the
string's maximum _size_ is 1024, as the code clearly intends to find
the terminating zero within the first 1024 characters, which would
yield a maximum length of 1023, not 1024.  But in that case, you're
looking at up to 1025 characters, one beyond the size of the string:
consider what happens in the case of a pointer that points to 1024
bytes of validly mapped memory, but those 1024 bytes end on a page
boundary, and the subsequent page is unmapped.

In either case, your example code appears to exhibit a classic
off-by-one error, and can be tricked into either looking beyond the
end of a valid memory object (if max len == 1023 and max size ==
1024), or failing to properly accept valid strings (if max len == 1024
and max size == 1025). The error here is in assuming that the return
value of `strnlen`, as you have used it, is enough to robustly
establish that the string ends within the acceptable bounds.

Sure, this is easy enough to fix in this case (hint: read up on the
`memchr` function). But beyond that simple error, C provides you with
_no way_ to determine whether a given `char *`, when provided as an
argument to an arbitrary function, points into a valid object of your
arbitrarily chosen length. So already your advice is impossible to
follow in the general case.

Now, coming to the above point number 2:

An example of openssh is given that it first does authentication and then it does encryption and this is insecure. 
I will investigate this and reply later.

Another area you don't touch on at all are TOCTOU bugs.

I suggest, perhaps, studying a bit more.

        - Dan C.


Current thread: