On Thu, Jul 22, 2021 at 12:11:17PM +0200, Vincent Lefevre wrote:
> I actually think that the compiler should warn in every case,
> but isn't able to detect all potential issues. The strfcpy
> definition seems wrong:
> 
> # define strfcpy(A,B,C) strncpy (A,B,C), *(A+(C)-1)=0
> 
> If A and B are buffers of size C, the strncpy call will yield a
> non-null terminated destination at this point, hence a potential
> warning (see the gcc(1) man page).
> 
> Note the *(A+(C)-1)=0. This means that A[(C)-1] will be set to 0.
> Thus you want to fill A[0] to A[(C)-2], i.e. copy (C)-1 bytes.
> So the definition should be
> 
> # define strfcpy(A,B,C) strncpy (A,B,(C)-1), *((A)+(C)-1)=0
> 
> Now, this doesn't solve the warnings, and I suppose that there is
> a bug in GCC.

After playing with gcc 10.2.1 I do think this warning is a bug in gcc.
(I didn't read all of the related bug reports in gcc's Bugzilla.)

The thing is that actually gcc knows the buffers A and B are the same
size (possibly because it is inlining one or more function calls).
This means that if B is a string then A cannot get truncated.  If you
remove the guard *(A+(C)-1)=0 from the macro definition then gcc does
not emit a warning for this case because it knows that A is not being
truncated.

However, this guard causes gcc to optimize the macro to your suggested
definition of it: that is, it modifies the strncpy call to copy at most
C-1 bytes because it knows the Cth byte will be overwritten.

Therefore, in the optimized strncpy call we are copying a string
that might be up to 255 bytes long (not including the terminating
nul) into a buffer of length 255 - which might cause the terminating
nul to get lost, which makes gcc issue a warning.

On systems that have memccpy, replacing the strncpy with memccpy is both
more efficient and warning-free on this version of gcc.  Recall that the
definition of strncpy is not just "copy at most n bytes" but also "pad
the destination to exactly n bytes".

imc

Reply via email to