Re: [PATCH] fs: lockd: avoid possible wrong NULL parameter

From: Nick Desaulniers
Date: Wed Aug 02 2023 - 17:40:39 EST


On Wed, Aug 2, 2023 at 3:25 AM Dan Carpenter <dan.carpenter@xxxxxxxxxx> wrote:
>
> On Wed, Aug 02, 2023 at 04:05:45PM +0800, Su Hui wrote:
> > clang's static analysis warning: fs/lockd/mon.c: line 293, column 2:
> > Null pointer passed as 2nd argument to memory copy function.
> >
> > Assuming 'hostname' is NULL and calling 'nsm_create_handle()', this will
> > pass NULL as 2nd argument to memory copy function 'memcpy()'. So return
> > NULL if 'hostname' is invalid.
> >
> > Fixes: 77a3ef33e2de ("NSM: More clean up of nsm_get_handle()")
> > Signed-off-by: Su Hui <suhui@xxxxxxxxxxxx>
> > ---
> > fs/lockd/mon.c | 3 +++
> > 1 file changed, 3 insertions(+)
> >
> > diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
> > index 1d9488cf0534..eebab013e063 100644
> > --- a/fs/lockd/mon.c
> > +++ b/fs/lockd/mon.c
> > @@ -358,6 +358,9 @@ struct nsm_handle *nsm_get_handle(const struct net *net,
> >
> > spin_unlock(&nsm_lock);
> >
> > + if (!hostname)
> > + return NULL;
> > +
> > new = nsm_create_handle(sap, salen, hostname, hostname_len);
>
> It's weird that this bug is from 2008 and we haven't found it in
> testing. Presumably if hostname is NULL then hostname_len would be zero
> and in that case, it's not actually a bug. It's allowed in the kernel
> to memcpy zero bytes from a NULL pointer.
>
> memcpy(dst, NULL, 0);
>
> Outside the kernel it's not allowed though.

I wonder what kind of implications that has on the compilers ability
to optimize libcalls to memcpy for targets that don't use
`-ffreestanding`. Hmm...

Though let's see what the C standard says, since that's what compilers
target, rather than consider specifics of glibc.

https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf
>> The memcpy function copies n characters from the object pointed to by s2 into the
>> object pointed to by s1. If copying takes place between objects that overlap, the behavior
>> is undefined.

So no mention about what assumptions can be made about source or
destination being NULL.

I noticed that the function in question already has a guard:
322 if (hostname && memchr(hostname, '/', hostname_len) != NULL) {

Which implies that hostname COULD be NULL.

Should this perhaps simply be rewritten as:

if (!hostname)
return NULL;
if (memchr(...) != NULL)
...

Rather than bury yet another guard for the same check further down in
the function? Check once and bail early.

>
> I noticed a related bug which Smatch doesn't find, because of how Smatch
> handles the dprintk macro.
>
> fs/lockd/host.c
> truct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
> 217 const size_t salen,
> 218 const unsigned short protocol,
> 219 const u32 version,
> 220 const char *hostname,
> 221 int noresvport,
> 222 struct net *net,
> 223 const struct cred *cred)
> 224 {
> 225 struct nlm_lookup_host_info ni = {
> 226 .server = 0,
> 227 .sap = sap,
> 228 .salen = salen,
> 229 .protocol = protocol,
> 230 .version = version,
> 231 .hostname = hostname,
> 232 .hostname_len = strlen(hostname),
> ^^^^^^^^
> Dereferenced
>
> 233 .noresvport = noresvport,
> 234 .net = net,
> 235 .cred = cred,
> 236 };
> 237 struct hlist_head *chain;
> 238 struct nlm_host *host;
> 239 struct nsm_handle *nsm = NULL;
> 240 struct lockd_net *ln = net_generic(net, lockd_net_id);
> 241
> 242 dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
> 243 (hostname ? hostname : "<none>"), version,
> ^^^^^^^^
> Checked too late.
>
> 244 (protocol == IPPROTO_UDP ? "udp" : "tcp"));
> 245
>
> regards,
> dan carpenter



--
Thanks,
~Nick Desaulniers