diff mbox series

libsepol: reject invalid roles before inverting

Message ID 20220117150200.24953-1-cgzones@googlemail.com (mailing list archive)
State Changes Requested
Headers show
Series libsepol: reject invalid roles before inverting | expand

Commit Message

Christian Göttsche Jan. 17, 2022, 3:02 p.m. UTC
Since the role datums have not been validated yet, they might be invalid
and set to an enormous high value. Inverting such an ebitmap will take
excessive amount of memory and time.

Found by oss-fuzz (#43709)
---
 libsepol/src/expand.c | 4 ++++
 1 file changed, 4 insertions(+)

Comments

James Carter Jan. 19, 2022, 7:17 p.m. UTC | #1
On Mon, Jan 17, 2022 at 9:34 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Since the role datums have not been validated yet, they might be invalid
> and set to an enormous high value. Inverting such an ebitmap will take
> excessive amount of memory and time.
>
> Found by oss-fuzz (#43709)
> ---
>  libsepol/src/expand.c | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
> index 898e6b87..3fc54af6 100644
> --- a/libsepol/src/expand.c
> +++ b/libsepol/src/expand.c
> @@ -2481,6 +2481,10 @@ int role_set_expand(role_set_t * x, ebitmap_t * r, policydb_t * out, policydb_t
>
>         /* if role is to be complimented, invert the entire bitmap here */
>         if (x->flags & ROLE_COMP) {
> +               /* inverting an ebitmap with an invalid highbit will take aeons */
> +               if (ebitmap_length(r) > p->p_roles.nprim)
> +                       return -1;
> +
>                 for (i = 0; i < ebitmap_length(r); i++) {
>                         if (ebitmap_get_bit(r, i)) {
>                                 if (ebitmap_set_bit(r, i, 0))
> --
> 2.34.1
>

One would think that ebitmap_length() would be the right function, but
actually it will return the highest position in the bitmap without
regard to whether it is set or not. Since the ebitmap has 64 bit
nodes, it will be a multiple of 64.

The function you want to use here is ebitmap_highest_set_bit().

Thanks,
Jim
Christian Göttsche Jan. 20, 2022, 4:15 p.m. UTC | #2
On Wed, 19 Jan 2022 at 20:18, James Carter <jwcart2@gmail.com> wrote:
>
> On Mon, Jan 17, 2022 at 9:34 PM Christian Göttsche
> <cgzones@googlemail.com> wrote:
> >
> > Since the role datums have not been validated yet, they might be invalid
> > and set to an enormous high value. Inverting such an ebitmap will take
> > excessive amount of memory and time.
> >
> > Found by oss-fuzz (#43709)
> > ---
> >  libsepol/src/expand.c | 4 ++++
> >  1 file changed, 4 insertions(+)
> >
> > diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
> > index 898e6b87..3fc54af6 100644
> > --- a/libsepol/src/expand.c
> > +++ b/libsepol/src/expand.c
> > @@ -2481,6 +2481,10 @@ int role_set_expand(role_set_t * x, ebitmap_t * r, policydb_t * out, policydb_t
> >
> >         /* if role is to be complimented, invert the entire bitmap here */
> >         if (x->flags & ROLE_COMP) {
> > +               /* inverting an ebitmap with an invalid highbit will take aeons */
> > +               if (ebitmap_length(r) > p->p_roles.nprim)
> > +                       return -1;
> > +
> >                 for (i = 0; i < ebitmap_length(r); i++) {
> >                         if (ebitmap_get_bit(r, i)) {
> >                                 if (ebitmap_set_bit(r, i, 0))
> > --
> > 2.34.1
> >
>
> One would think that ebitmap_length() would be the right function, but
> actually it will return the highest position in the bitmap without
> regard to whether it is set or not. Since the ebitmap has 64 bit
> nodes, it will be a multiple of 64.
>
> The function you want to use here is ebitmap_highest_set_bit().

On a second thought: does this inverting even work?

Consider `p->p_roles.nprim` being 42, the highest bit in the current
ebitmap being 32, and thus the length/highbit being 64.
The highest bit 32 is less than 42 so that's fine.
But then the inversion inverts all bits up to `ebitmap_length()`
(=64), so now the highest bit is 64, which is bigger than
`p->p_roles.nprim`.
The ebitmap now has a role set that is not defined, some further
operations might fail, and any subsequent validation will fail (e.g.
in `validate_ebitmap()`).
git-blame[1] shows this code was introduced with the initial import
from svn and also the flag ROLE_COMP is otherwise unused (with an
exception in the test binary dismod).
checkpolicy(8) does not accept role complements, e.g. `user sys_user
roles ~sys_role;` or `allow role1 ~role2;` are invalid.

Maybe the inversion would be safe if the upper bound was
`p->p_roles.nprim` not `ebitmap_length()`?


[1]: https://github.com/SELinuxProject/selinux/blame/master/libsepol/src/expand.c

>
> Thanks,
> Jim
James Carter Jan. 20, 2022, 5:37 p.m. UTC | #3
On Thu, Jan 20, 2022 at 11:15 AM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> On Wed, 19 Jan 2022 at 20:18, James Carter <jwcart2@gmail.com> wrote:
> >
> > On Mon, Jan 17, 2022 at 9:34 PM Christian Göttsche
> > <cgzones@googlemail.com> wrote:
> > >
> > > Since the role datums have not been validated yet, they might be invalid
> > > and set to an enormous high value. Inverting such an ebitmap will take
> > > excessive amount of memory and time.
> > >
> > > Found by oss-fuzz (#43709)
> > > ---
> > >  libsepol/src/expand.c | 4 ++++
> > >  1 file changed, 4 insertions(+)
> > >
> > > diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
> > > index 898e6b87..3fc54af6 100644
> > > --- a/libsepol/src/expand.c
> > > +++ b/libsepol/src/expand.c
> > > @@ -2481,6 +2481,10 @@ int role_set_expand(role_set_t * x, ebitmap_t * r, policydb_t * out, policydb_t
> > >
> > >         /* if role is to be complimented, invert the entire bitmap here */
> > >         if (x->flags & ROLE_COMP) {
> > > +               /* inverting an ebitmap with an invalid highbit will take aeons */
> > > +               if (ebitmap_length(r) > p->p_roles.nprim)
> > > +                       return -1;
> > > +
> > >                 for (i = 0; i < ebitmap_length(r); i++) {
> > >                         if (ebitmap_get_bit(r, i)) {
> > >                                 if (ebitmap_set_bit(r, i, 0))
> > > --
> > > 2.34.1
> > >
> >
> > One would think that ebitmap_length() would be the right function, but
> > actually it will return the highest position in the bitmap without
> > regard to whether it is set or not. Since the ebitmap has 64 bit
> > nodes, it will be a multiple of 64.
> >
> > The function you want to use here is ebitmap_highest_set_bit().
>
> On a second thought: does this inverting even work?
>
> Consider `p->p_roles.nprim` being 42, the highest bit in the current
> ebitmap being 32, and thus the length/highbit being 64.
> The highest bit 32 is less than 42 so that's fine.
> But then the inversion inverts all bits up to `ebitmap_length()`
> (=64), so now the highest bit is 64, which is bigger than
> `p->p_roles.nprim`.
> The ebitmap now has a role set that is not defined, some further
> operations might fail, and any subsequent validation will fail (e.g.
> in `validate_ebitmap()`).
> git-blame[1] shows this code was introduced with the initial import
> from svn and also the flag ROLE_COMP is otherwise unused (with an
> exception in the test binary dismod).
> checkpolicy(8) does not accept role complements, e.g. `user sys_user
> roles ~sys_role;` or `allow role1 ~role2;` are invalid.
>
> Maybe the inversion would be safe if the upper bound was
> `p->p_roles.nprim` not `ebitmap_length()`?
>

Yes, it should be using p->p_roles.nprim and not ebitmap_length(). The
function type_set_expand() uses p->p_types.nprim as it should.

Thanks,
Jim


>
> [1]: https://github.com/SELinuxProject/selinux/blame/master/libsepol/src/expand.c
>
> >
> > Thanks,
> > Jim
diff mbox series

Patch

diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
index 898e6b87..3fc54af6 100644
--- a/libsepol/src/expand.c
+++ b/libsepol/src/expand.c
@@ -2481,6 +2481,10 @@  int role_set_expand(role_set_t * x, ebitmap_t * r, policydb_t * out, policydb_t
 
 	/* if role is to be complimented, invert the entire bitmap here */
 	if (x->flags & ROLE_COMP) {
+		/* inverting an ebitmap with an invalid highbit will take aeons */
+		if (ebitmap_length(r) > p->p_roles.nprim)
+			return -1;
+
 		for (i = 0; i < ebitmap_length(r); i++) {
 			if (ebitmap_get_bit(r, i)) {
 				if (ebitmap_set_bit(r, i, 0))