Re: [bug report] nfs: Convert to new fscache volume/cookie API

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Dec 1, 2021 at 3:55 AM Dan Carpenter <dan.carpenter@xxxxxxxxxx> wrote:
>
> Hi Dave,
>
> The patch 1234f5681081: "nfs: Convert to new fscache volume/cookie
> API" from Nov 14, 2020, leads to the following Smatch static checker
> warning:
>
>         fs/nfs/fscache.c:32 nfs_append_int()
>         warn: array off by one? 'key[(*_len)++]'
>
> This is an unpublished check which assumes all > comparisons are wrong
> until proven otherwise.
>
> fs/nfs/fscache.c
>     27 static bool nfs_append_int(char *key, int *_len, unsigned long long x)
>     28 {
>     29         if (*_len > NFS_MAX_KEY_LEN)
>     30                 return false;
>     31         if (x == 0)
> --> 32                 key[(*_len)++] = ',';
>     33         else
>     34                 *_len += sprintf(key + *_len, ",%llx", x);
>     35         return true;
>     36 }
>
> This function is very badly broken.  As the checker suggests, the >
> should be >= to prevent an array overflow.  But it's actually off by
> two because we have to leave space for the NUL terminator so the buffer
> is full when "*_len == NFS_MAX_KEY_LEN - 1".  That means the check
> should be:
>
>         if (*_len >= NFS_MAX_KEY_LEN - 1)
>                 return false;
>
> Except NFS_MAX_KEY_LEN is not the correct limit.  The buffer is larger
> than that.
>
> Unfortunately if we fixed the array overflow check then the sprintf()
> will now corrupt memory...  There is a missing check after the sprintf()
> so it does not tell you if we printed everything or not.  It just
> returns true and the next caller detects the overflow.
>
> I feel like append() functions are easier to use if we keep the start
> pointer and and len value constant and just modify the *offset.
>
> static bool nfs_append_int(char *key, int *off, int len, unsigned long long x)
> {
>         if (*off >= len - 1)
>                 return false;
>
>         if (x == 0)
>                 key[(*off)++] = ',';
>         else
>                 *off + snprintf(key + *off, len - *off, ",%llx", x);
>
>         if (*off >= len)
>                 return false;
>         return true;
> }
>
> [ snip ]
>
>     86  void nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, int ulen)
>     87  {
>     88          struct nfs_server *nfss = NFS_SB(sb);
>     89          unsigned int len = 3;
>     90          char *key;
>     91
>     92          if (uniq) {
>     93                  nfss->fscache_uniq = kmemdup_nul(uniq, ulen, GFP_KERNEL);
>     94                  if (!nfss->fscache_uniq)
>     95                          return;
>     96          }
>     97
>     98          key = kmalloc(NFS_MAX_KEY_LEN + 24, GFP_KERNEL);
>     99          if (!key)
>    100                  return;
>    101
>    102          memcpy(key, "nfs", 3);
>    103          if (!nfs_fscache_get_client_key(nfss->nfs_client, key, &len) ||
>    104              !nfs_append_int(key, &len, nfss->fsid.major) ||
>
> So then we do:
>
>         len = NFS_MAX_KEY_LEN + 24;
>
> It's not totally clear to me where the 24 comes from or I would have
> sent a patch instead of a bug report.
>
>         memcpy(key, "nfs", 3);
>         off = 3;
>
>         if (!nfs_fscache_get_client_key(nfss->nfs_client, key, &off) ||
>             !nfs_append_int(key, &off, len, nfss->fsid.major) ||
>             !nfs_append_int(key, &off, len, nfss->fsid.minor) ||
>
>    105              !nfs_append_int(key, &len, nfss->fsid.minor) ||
>    106              !nfs_append_int(key, &len, sb->s_flags & NFS_SB_MASK) ||
>    107              !nfs_append_int(key, &len, nfss->flags) ||
>    108              !nfs_append_int(key, &len, nfss->rsize) ||
>    109              !nfs_append_int(key, &len, nfss->wsize) ||
>    110              !nfs_append_int(key, &len, nfss->acregmin) ||
>    111              !nfs_append_int(key, &len, nfss->acregmax) ||
>    112              !nfs_append_int(key, &len, nfss->acdirmin) ||
>    113              !nfs_append_int(key, &len, nfss->acdirmax) ||
>    114              !nfs_append_int(key, &len, nfss->client->cl_auth->au_flavor))
>    115                  goto out;
>    116
>    117          if (ulen > 0) {
>    118                  if (ulen > NFS_MAX_KEY_LEN - len)
>
> This check is also off.  It does not take into consideration the comma
> or the NUL terminator.  We need a "+ 2".
>
>                         if (ulen + 2 > len - off) {
>
>    119                          goto out;
>    120                  key[len++] = ',';
>    121                  memcpy(key + len, uniq, ulen);
>    122                  len += ulen;
>    123          }
>    124          key[len] = 0;
>    125
>    126          /* create a cache index for looking up filehandles */
>
> regards,
> dan carpenter
>

Thanks for the report.

The nfs_append_int() was actually a portion that David Howells
wrote in the latest round of fscache API conversion.
So I'm not sure if he wants to fix this up or if I should give it a go.




[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux