selinux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: William Roberts <bill.c.roberts@gmail.com>
To: Nicolas Iooss <nicolas.iooss@m4x.org>
Cc: SElinux list <selinux@vger.kernel.org>
Subject: Re: [PATCH 4/6] libsepol/cil: fix NULL pointer dereference when parsing an improper integer
Date: Sun, 3 Jan 2021 12:32:17 -0600	[thread overview]
Message-ID: <CAFftDdrGoQezmVSOnrFrPKaOnS3pejQXzYpfjwQ+QBHH_Pv02w@mail.gmail.com> (raw)
In-Reply-To: <CAJfZ7=n5va_a9c3Z3QKxaDSZBk4Q5VT98C28VbuXaOYkwUy31A@mail.gmail.com>

On Sat, Jan 2, 2021 at 5:13 AM Nicolas Iooss <nicolas.iooss@m4x.org> wrote:
>
> On Thu, Dec 31, 2020 at 4:04 PM William Roberts
> <bill.c.roberts@gmail.com> wrote:
> >
> > On Wed, Dec 30, 2020 at 4:09 AM Nicolas Iooss <nicolas.iooss@m4x.org> wrote:
> > >
> > > OSS-Fuzz found a NULL pointer dereference when the CIL compiler tries to
> > > compile a policy with an invalid integer:
> > >
> > >     $ echo '(ioportcon(2())n)' > tmp.cil
> > >     $ secilc tmp.cil
> > >     Segmentation fault (core dumped)
> > >
> > > This is because strtol() is called with a NULL pointer, in
> > > cil_fill_integer().
> > >
> > > Fix this by checking that int_node->data is not NULL. While at it, use
> > > strtoul() instead of strtol() to parse an unsigned integer.
> > >
> > > Fixes: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=28456
> > > Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
> > > ---
> > >  libsepol/cil/src/cil_build_ast.c | 10 +++++-----
> > >  1 file changed, 5 insertions(+), 5 deletions(-)
> > >
> > > diff --git a/libsepol/cil/src/cil_build_ast.c b/libsepol/cil/src/cil_build_ast.c
> > > index 67801def0dc0..0c9015cef578 100644
> > > --- a/libsepol/cil/src/cil_build_ast.c
> > > +++ b/libsepol/cil/src/cil_build_ast.c
> > > @@ -5566,15 +5566,15 @@ int cil_fill_integer(struct cil_tree_node *int_node, uint32_t *integer, int base
> > >  {
> > >         int rc = SEPOL_ERR;
> > >         char *endptr = NULL;
> > > -       int val;
> > > +       unsigned long val;
> > >
> > > -       if (int_node == NULL || integer == NULL) {
> > > +       if (int_node == NULL || int_node->data == NULL || integer == NULL) {
> > >                 goto exit;
> > >         }
> > >
> > >         errno = 0;
> > > -       val = strtol(int_node->data, &endptr, base);
> > > -       if (errno != 0 || endptr == int_node->data || *endptr != '\0') {
> > > +       val = strtoul(int_node->data, &endptr, base);
> > > +       if (errno != 0 || endptr == int_node->data || *endptr != '\0' || val > UINT32_MAX) {
> >
> > I wonder if compilers/static analysis tools will balk on this as
> > strtoul's return, an unsigned long,
> > on a 32 bit machine will be 32 bits, so this could have a dead
> > expression as val > UINT32_MAX
> > will always be false. Perhaps use the strtoull variant to always have 64 bits?
>
> In my humble opinion, a compiler or a static analyzer which warn about
> the fact that "comparing an unsigned long value to UINT32_MAX is
> always true" have an issue, because this seems to be the most natural
> way of checking that a potentially-64-bit number can be safely
> downcasted to 32 bits.
>
> I find the suggestion of using strtoull to get a 32-bit integer to be
> very hackish, considering that on 32-bit systems, strtoul does the job
> fine (returning with errno = ERANGE when the value is too large) and
> 64-bit integers are using pairs of registers to be stored. If this
> code ever causes issues with some compilers, some preprocessor logic
> (such as "#if ULONG_MAX > UINT32_MAX") could be added to hide "val >
> UINT32_MAX" from 32-bit compilers. Nevertheless in an effort to keep
> the amount of preprocessor code as low as possible, I do not want to
> include such logic right now.

The reason I bring this up, is that I've personally encountered this
on Android when porting
libraries before. But these we're with much older GCC/CLANG variants.
When Android's
build went from gcc to clang, I remember clang being extra noisy and
these -Wtype-limits
warnings being a constant battle when trying to enable -Wextra.

t really boils down to the warning (per the man page):
-Wtype-limits
Warn if a comparison is always true or always false due to the limited
range of the data type, but do not warn for constant expressions. For
example, warn if an unsigned variable is compared against zero with <
or >=. This warning is also enabled by -Wextra.

But I tested this with an arm32v7 Docker Container running Ubuntu 20.04 using:
- GCC 9.3.0
- CLANG 10.0

as well as the NDK clang version Android (6454773 based on r365631c2)
clang version 9.0.8
cross compiling from x86_64 machine and it doesn't reproduce.

I wonder if they changed the behavior a bit because of having to
deal with this error all the time, and it was no fun. What's more
perplexing is that the uint8_t and uint16_t cases
trigger the warning and the uint32_t and uint64_t cases do not. I can
understand skipping the warning on
the unsigned long because of the word length changing from 8 to 4
bytes, and the 4 byte check
ending up being always false and harmless, but in the fixed width
case... I wonder why the compiler gods chose so.

        uint8_t y = (uint8_t)argc;
        if(y > UINT8_MAX) { printf("foo: %u\n", y); }  // always false

        uint16_t w = (uint16_t)argc;
        if(w > UINT16_MAX) { printf("foo: %u\n", w); }  // always false

        uint32_t q = (uint32_t)argc;
        if(q > UINT32_MAX) { printf("foo: %u\n", q); }  // always false

        uint64_t z = (uint64_t)argc;
        if(z > UINT64_MAX) { printf("foo: %llu\n", z); }  // always false

a.c:23:7: warning: comparison is always false due to limited range of
data type [-Wtype-limits]
   23 |  if(y > UINT8_MAX) { printf("foo: %u\n", y); }  // always false

>
> In short, I am not willing to change this patch unless someone reports
> a regression due to "val > UINT32_MAX".

Based on my tests with modern versions of toolchains, I think we're
fine. I don't know why
we're fine, but we appear so.

>
> Thanks for your review!
> Nicolas

ack for the whole series since this was my only worry.

>
> > >                 rc = SEPOL_ERR;
> > >                 goto exit;
> > >         }
> > > @@ -5594,7 +5594,7 @@ int cil_fill_integer64(struct cil_tree_node *int_node, uint64_t *integer, int ba
> > >         char *endptr = NULL;
> > >         uint64_t val;
> > >
> > > -       if (int_node == NULL || integer == NULL) {
> > > +       if (int_node == NULL || int_node->data == NULL || integer == NULL) {
> > >                 goto exit;
> > >         }
> > >
> > > --
> > > 2.29.2
> > >
>

  reply	other threads:[~2021-01-03 18:33 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-30 10:07 [PATCH 1/6] libsepol: do not decode out-of-bound rolebounds Nicolas Iooss
2020-12-30 10:07 ` [PATCH 2/6] libsepol: ensure that hashtab_search is not called with a NULL key Nicolas Iooss
2021-01-04 16:31   ` James Carter
2021-01-06  8:12     ` Nicolas Iooss
2020-12-30 10:07 ` [PATCH 3/6] libsepol/cil: constify some strings Nicolas Iooss
2021-01-04 16:33   ` James Carter
2021-01-05 16:07     ` James Carter
2020-12-30 10:07 ` [PATCH 4/6] libsepol/cil: fix NULL pointer dereference when parsing an improper integer Nicolas Iooss
2020-12-31 15:04   ` William Roberts
2021-01-02 11:13     ` Nicolas Iooss
2021-01-03 18:32       ` William Roberts [this message]
2021-01-04 16:43   ` James Carter
2021-01-05 12:51     ` William Roberts
2020-12-30 10:07 ` [PATCH 5/6] libsepol/cil: fix out-of-bound read in cil_print_recursive_blockinherit Nicolas Iooss
2021-01-04 18:17   ` James Carter
2021-01-05 16:08     ` James Carter
2020-12-30 10:07 ` [PATCH 6/6] libsepol/cil: destroy perm_datums when __cil_resolve_perms fails Nicolas Iooss
2020-12-31 15:05   ` William Roberts
2021-01-04 18:18   ` James Carter
2021-01-05 16:08     ` James Carter
2021-01-04 15:48 ` [PATCH 1/6] libsepol: do not decode out-of-bound rolebounds James Carter
2021-01-04 15:51   ` James Carter
2021-01-06  8:05     ` Nicolas Iooss

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAFftDdrGoQezmVSOnrFrPKaOnS3pejQXzYpfjwQ+QBHH_Pv02w@mail.gmail.com \
    --to=bill.c.roberts@gmail.com \
    --cc=nicolas.iooss@m4x.org \
    --cc=selinux@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).