Saturday, 20 May 2017

c - strlcat(): is dst always NUL_terminated? What are "size" and the returned value?



I have to implement my own version of the strlcat() function from the standard C library.



size_t     strlcat(char * restrict dst, const char * restrict src, size_t size);


I have two questions about how it works :






In my man I have the following :




strlcat() take the full size of the buffer (not just the length) and
guarantee to NUL-terminate the result (as long as size is larger than 0 or, in the case of strlcat(), as long as ther is at least one byte free in dst).




and :





The strlcat() function appends the NUL-terminated string src to the
end of dst. It will append at most size - strlen(dst) - 1 bytes,
NUL-terminating the result.




but also :




Note however, that if strlcat() traverses size characters without

finding a NUL, the length of the string is considered to be size and
the destination string will not be NUL-terminated (since there was no
space for the NUL).




So, should I NUL_terminate dst in EVERY case?
On one hand it says there is a case where the dst string isn't NUL_terminated.
On the other hand the man says that strlcat() guarantees that dst will be NUL_terminated, and wouldn't a non NUL_terminated string be pretty insecure?



Could someone give me an example where this case would occur?






Here are the results I get with some tests :



Before :                || After :
dst | src | size || dst | return
------------------------||--------------------
dst\0 | src\0 | 0 || dst\0 | 3
dst\0 | src\0 | 1 || dst\0 | 4

dst\0 | src\0 | 2 || dst\0 | 5
dst\0 | src\0 | 3 || dst\0 | 6
dst\0 | src\0 | 4 || dst\0 | 6
dst\0 | src\0 | 5 || dsts\0 | 6
dst\0 | src\0 | 6 || dstsr\0 | 6
dst\0 | src\0 | 7 || dstsrc\0 | 6
dst\0 | src\0 | 8 || dstsrc\0 | 6


From the man again :





[The strlcat() function] return the total length of the string [it
tries] to create. For strlcat() that means the initial length of dst
plus the length of src.




dst's and src's sizes are constant in my tests (3 and 3).
So why are there cases where the returned value is different from 6?




Isn't it rather (len(dst) + min(size, len(src))?



What does size represents?




The strlcat() function appends the NUL-terminated string src to the
end of dst. It will append at most size - strlen(dst) - 1 bytes,
NUL-terminating the result.





So size should be the the length ('\0' char included) dst is allowed to be at the end? Is that right?


Answer



1. Does strlcat always null-terminate dst?



dst will be null-terminated by strlcat if strlcat modifies dst. Strlcat does not modify dst if dst is already fully occupied. dst is considered fully-occupied if no NUL is found in the first size - 1 bytes of dst (or if size is 0).



So there are two cases in which dst will not be null-terminated by strlcat. One is that dst is a null-terminated string of exactly size - 1 bytes, in which case it is not modified and continues to be null-terminated. The second case is that dst was not null-terminated initially, and in that case it will still be unterminated after the call to strlcat.



2. How is the length of the original destination string measured?




size is expected to be the size of the memory region containing dst, so that dst[size] is assumed to be an invalid memory reference. Consequently, if dst starts out as a valid (and therefore null-terminated) string, its length will be strictly less than size and strlcat will use strlen(dst) as its length. If dst is not valid, then its size is assumed to be size for the purposes of the return value. In this case, dst will not be modified. See above.


No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...