| commit adc3765ce2cd74864cae843ad33619ff926a7eae
| Author: Kim B. Heino <[email protected]>
| Date:   Wed Oct 4 10:53:03 2017 +0300
| 
|     addconn: 64kB is not enough for everyone
|     
|     On systems where sizeof(int) == sizeof(ssize_t) netlink.h:NLMSG_OK() gives
|     a warning "comparison between signed and unsigned integer expressions".
|     This can be avoided by casting ssize_t to size_t. Negative values are 
already
|     checked so this cast does not loose sign bit.

Warning: the following argument is long and intricate.  It might have
bugs.  It is also boring.

What's GCC's warning about?  C just cannot compare an unsigned and a
signed type correctly UNLESS the unsigned type is widened to a signed
type.  When does widening occur?  When the other operand's type is
wider than the unsigned type or the unsigned type is narrower than
int.

  int < unsigned // bad
  int < unsigned short // OK, as long as sizeof(int) > sizeof(short)
  size_t < ssize_t // bad
  long < size_t // OK, as long as sizeof(long) > sizeof(size_t)

By "bad", I mean that the result of the comparision will be wrong if
the signed operand happens to have a negative value.  Generally a
compiler assumes that a value with a signed type might have a
negative value.  Compilers could be smarter.

GCC (and clang, etc.) could now be smarter.  They could notice that
(int) sizeof(struct nlmsghdr) is of a signed type but the value will
never be negative.  In this case, the unsigned comparision (as
prescribed by the C standard) would always yield the correct result
and no warning is necessary.  I'm assuming that this is not the case.

/usr/include/linux/netlink.h:

#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
                           (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
                           (nlh)->nlmsg_len <= (len))

and
        __u32 nlmsg_len; /* Length of message including header */

Let's look at the two conjuncts that involve len.

1) (len) >= (int)sizeof(struct nlmsghdr)

2) (nlh)->nlmsg_len <= (len)

So:
1 compares len with int
2 compares len with uint32_t

To avoid warnings, typeof(len) must be signed and wider than uint32_t
or unsigned and narrower than int.

Before the change of this commit, I chose "unsigned and narrower than
int" (unsigned short).

This commit has chosen size_t, which is unsigned.  But is it wider than
uint32_t?  Not on x86-32.  Or on other pure 32-bit architctures
(32-bit ARM).

So this commit message is wrong.  Casting to size_t should not
suppress warnings on all machines.  (However, the resulting object
code should run correctly on all machines.)

I guess that the safest fix is to use type int64_t.  It is signed and
surely wider than uint32_t.  In some distant/theoretical future it
might not be as wide as ssize_t.  At that time, we can drop the cast
altogether.

I admit that this is quite annoying.  The flaw is in the definition of
NLMSG_OK.  It does not even comform to its own documentation,
netlink(3)

       int NLMSG_OK(struct nlmsghdr *nlh, int len);

The netlink maintainers refuse to fix this.

The GCC warning is newer than netlink.  But the warning is correct and
would have been useful all along.

_______________________________________________
Swan-dev mailing list
[email protected]
https://lists.libreswan.org/mailman/listinfo/swan-dev

Reply via email to