2019-07-22 12:46:56

by Gote, Nitin R

[permalink] [raw]
Subject: [PATCH] selinux: convert struct sidtab count to refcount_t

refcount_t type and corresponding API should be
used instead of atomic_t when the variable is used as
a reference counter. This allows to avoid accidental
refcounter overflows that might lead to use-after-free
situations.

Signed-off-by: NitinGote <[email protected]>
---
security/selinux/ss/sidtab.c | 16 ++++++++--------
security/selinux/ss/sidtab.h | 2 +-
2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
index e63a90ff2728..20fe235c6c71 100644
--- a/security/selinux/ss/sidtab.c
+++ b/security/selinux/ss/sidtab.c
@@ -29,7 +29,7 @@ int sidtab_init(struct sidtab *s)
for (i = 0; i < SECINITSID_NUM; i++)
s->isids[i].set = 0;

- atomic_set(&s->count, 0);
+ refcount_set(&s->count, 0);

s->convert = NULL;

@@ -130,7 +130,7 @@ static struct context *sidtab_do_lookup(struct sidtab *s, u32 index, int alloc)

static struct context *sidtab_lookup(struct sidtab *s, u32 index)
{
- u32 count = (u32)atomic_read(&s->count);
+ u32 count = refcount_read(&s->count);

if (index >= count)
return NULL;
@@ -245,7 +245,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
u32 *index)
{
unsigned long flags;
- u32 count = (u32)atomic_read(&s->count);
+ u32 count = (u32)refcount_read(&s->count);
u32 count_locked, level, pos;
struct sidtab_convert_params *convert;
struct context *dst, *dst_convert;
@@ -272,7 +272,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
spin_lock_irqsave(&s->lock, flags);

convert = s->convert;
- count_locked = (u32)atomic_read(&s->count);
+ count_locked = (u32)refcount_read(&s->count);
level = sidtab_level_from_count(count_locked);

/* if count has changed before we acquired the lock, then catch up */
@@ -315,7 +315,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
}

/* at this point we know the insert won't fail */
- atomic_set(&convert->target->count, count + 1);
+ refcount_set(&convert->target->count, count + 1);
}

if (context->len)
@@ -328,7 +328,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
/* write entries before writing new count */
smp_wmb();

- atomic_set(&s->count, count + 1);
+ refcount_set(&s->count, count + 1);

rc = 0;
out_unlock:
@@ -418,7 +418,7 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
return -EBUSY;
}

- count = (u32)atomic_read(&s->count);
+ count = (u32)refcount_read(&s->count);
level = sidtab_level_from_count(count);

/* allocate last leaf in the new sidtab (to avoid race with
@@ -431,7 +431,7 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
}

/* set count in case no new entries are added during conversion */
- atomic_set(&params->target->count, count);
+ refcount_set(&params->target->count, count);

/* enable live convert of new entries */
s->convert = params;
diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h
index bbd5c0d1f3bd..68dd96a5beba 100644
--- a/security/selinux/ss/sidtab.h
+++ b/security/selinux/ss/sidtab.h
@@ -70,7 +70,7 @@ struct sidtab_convert_params {

struct sidtab {
union sidtab_entry_inner roots[SIDTAB_MAX_LEVEL + 1];
- atomic_t count;
+ refcount_t count;
struct sidtab_convert_params *convert;
spinlock_t lock;

--
2.17.1


2019-07-22 13:34:05

by Ondrej Mosnacek

[permalink] [raw]
Subject: Re: [PATCH] selinux: convert struct sidtab count to refcount_t

On Mon, Jul 22, 2019 at 1:35 PM NitinGote <[email protected]> wrote:
> refcount_t type and corresponding API should be
> used instead of atomic_t when the variable is used as
> a reference counter. This allows to avoid accidental
> refcounter overflows that might lead to use-after-free
> situations.
>
> Signed-off-by: NitinGote <[email protected]>

Nack.

The 'count' variable is not used as a reference counter here. It
tracks the number of entries in sidtab, which is a very specific
lookup table that can only grow (the count never decreases). I only
made it atomic because the variable is read outside of the sidtab's
spin lock and thus the reads and writes to it need to be guaranteed to
be atomic. The counter is only updated under the spin lock, so
insertions do not race with each other.

Your patch, however, lead me to realize that I forgot to guard against
overflow above SIDTAB_MAX when a new entry is being inserted. It is
extremely unlikely to happen in practice, but should be fixed anyway.
I'll send a patch shortly.

> ---
> security/selinux/ss/sidtab.c | 16 ++++++++--------
> security/selinux/ss/sidtab.h | 2 +-
> 2 files changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
> index e63a90ff2728..20fe235c6c71 100644
> --- a/security/selinux/ss/sidtab.c
> +++ b/security/selinux/ss/sidtab.c
> @@ -29,7 +29,7 @@ int sidtab_init(struct sidtab *s)
> for (i = 0; i < SECINITSID_NUM; i++)
> s->isids[i].set = 0;
>
> - atomic_set(&s->count, 0);
> + refcount_set(&s->count, 0);
>
> s->convert = NULL;
>
> @@ -130,7 +130,7 @@ static struct context *sidtab_do_lookup(struct sidtab *s, u32 index, int alloc)
>
> static struct context *sidtab_lookup(struct sidtab *s, u32 index)
> {
> - u32 count = (u32)atomic_read(&s->count);
> + u32 count = refcount_read(&s->count);
>
> if (index >= count)
> return NULL;
> @@ -245,7 +245,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
> u32 *index)
> {
> unsigned long flags;
> - u32 count = (u32)atomic_read(&s->count);
> + u32 count = (u32)refcount_read(&s->count);
> u32 count_locked, level, pos;
> struct sidtab_convert_params *convert;
> struct context *dst, *dst_convert;
> @@ -272,7 +272,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
> spin_lock_irqsave(&s->lock, flags);
>
> convert = s->convert;
> - count_locked = (u32)atomic_read(&s->count);
> + count_locked = (u32)refcount_read(&s->count);
> level = sidtab_level_from_count(count_locked);
>
> /* if count has changed before we acquired the lock, then catch up */
> @@ -315,7 +315,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
> }
>
> /* at this point we know the insert won't fail */
> - atomic_set(&convert->target->count, count + 1);
> + refcount_set(&convert->target->count, count + 1);
> }
>
> if (context->len)
> @@ -328,7 +328,7 @@ static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
> /* write entries before writing new count */
> smp_wmb();
>
> - atomic_set(&s->count, count + 1);
> + refcount_set(&s->count, count + 1);
>
> rc = 0;
> out_unlock:
> @@ -418,7 +418,7 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
> return -EBUSY;
> }
>
> - count = (u32)atomic_read(&s->count);
> + count = (u32)refcount_read(&s->count);
> level = sidtab_level_from_count(count);
>
> /* allocate last leaf in the new sidtab (to avoid race with
> @@ -431,7 +431,7 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
> }
>
> /* set count in case no new entries are added during conversion */
> - atomic_set(&params->target->count, count);
> + refcount_set(&params->target->count, count);
>
> /* enable live convert of new entries */
> s->convert = params;
> diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h
> index bbd5c0d1f3bd..68dd96a5beba 100644
> --- a/security/selinux/ss/sidtab.h
> +++ b/security/selinux/ss/sidtab.h
> @@ -70,7 +70,7 @@ struct sidtab_convert_params {
>
> struct sidtab {
> union sidtab_entry_inner roots[SIDTAB_MAX_LEVEL + 1];
> - atomic_t count;
> + refcount_t count;
> struct sidtab_convert_params *convert;
> spinlock_t lock;
>
> --
> 2.17.1
>

Thanks,

--
Ondrej Mosnacek <omosnace at redhat dot com>
Software Engineer, Security Technologies
Red Hat, Inc.

2019-07-23 08:42:05

by Paul Moore

[permalink] [raw]
Subject: Re: [PATCH] selinux: convert struct sidtab count to refcount_t

On Mon, Jul 22, 2019 at 9:18 AM Ondrej Mosnacek <[email protected]> wrote:
> On Mon, Jul 22, 2019 at 1:35 PM NitinGote <[email protected]> wrote:
> > refcount_t type and corresponding API should be
> > used instead of atomic_t when the variable is used as
> > a reference counter. This allows to avoid accidental
> > refcounter overflows that might lead to use-after-free
> > situations.
> >
> > Signed-off-by: NitinGote <[email protected]>
>
> Nack.
>
> The 'count' variable is not used as a reference counter here. It
> tracks the number of entries in sidtab, which is a very specific
> lookup table that can only grow (the count never decreases). I only
> made it atomic because the variable is read outside of the sidtab's
> spin lock and thus the reads and writes to it need to be guaranteed to
> be atomic. The counter is only updated under the spin lock, so
> insertions do not race with each other.

Agreed, this should be changed to use refcount_t.

--
paul moore
http://www.paul-moore.com

2019-07-23 10:05:16

by Gote, Nitin R

[permalink] [raw]
Subject: RE: [PATCH] selinux: convert struct sidtab count to refcount_t



> -----Original Message-----
> From: Ondrej Mosnacek [mailto:[email protected]]
> Sent: Monday, July 22, 2019 6:48 PM
> To: Gote, Nitin R <[email protected]>
> Cc: Kees Cook <[email protected]>; kernel-
> [email protected]; Paul Moore <[email protected]>;
> Stephen Smalley <[email protected]>; Eric Paris <[email protected]>;
> SElinux list <[email protected]>; Linux kernel mailing list <linux-
> [email protected]>
> Subject: Re: [PATCH] selinux: convert struct sidtab count to refcount_t
>
> On Mon, Jul 22, 2019 at 1:35 PM NitinGote <[email protected]> wrote:
> > refcount_t type and corresponding API should be used instead of
> > atomic_t when the variable is used as a reference counter. This allows
> > to avoid accidental refcounter overflows that might lead to
> > use-after-free situations.
> >
> > Signed-off-by: NitinGote <[email protected]>
>
> Nack.
>
> The 'count' variable is not used as a reference counter here. It tracks the
> number of entries in sidtab, which is a very specific lookup table that can
> only grow (the count never decreases). I only made it atomic because the
> variable is read outside of the sidtab's spin lock and thus the reads and
> writes to it need to be guaranteed to be atomic. The counter is only updated
> under the spin lock, so insertions do not race with each other.

Agreed. Thanks for clarification.
I'm going to discontinue this patch.

>
> Your patch, however, lead me to realize that I forgot to guard against
> overflow above SIDTAB_MAX when a new entry is being inserted. It is
> extremely unlikely to happen in practice, but should be fixed anyway.
> I'll send a patch shortly.
>

Thank you.

> > ---
> > security/selinux/ss/sidtab.c | 16 ++++++++--------
> > security/selinux/ss/sidtab.h | 2 +-
> > 2 files changed, 9 insertions(+), 9 deletions(-)
> >
> > diff --git a/security/selinux/ss/sidtab.c
> > b/security/selinux/ss/sidtab.c index e63a90ff2728..20fe235c6c71 100644
> > --- a/security/selinux/ss/sidtab.c
> > +++ b/security/selinux/ss/sidtab.c
> > @@ -29,7 +29,7 @@ int sidtab_init(struct sidtab *s)
> > for (i = 0; i < SECINITSID_NUM; i++)
> > s->isids[i].set = 0;
> >
> > - atomic_set(&s->count, 0);
> > + refcount_set(&s->count, 0);
> >
> > s->convert = NULL;
> >
> > @@ -130,7 +130,7 @@ static struct context *sidtab_do_lookup(struct
> > sidtab *s, u32 index, int alloc)
> >
> > static struct context *sidtab_lookup(struct sidtab *s, u32 index) {
> > - u32 count = (u32)atomic_read(&s->count);
> > + u32 count = refcount_read(&s->count);
> >
> > if (index >= count)
> > return NULL;
> > @@ -245,7 +245,7 @@ static int sidtab_reverse_lookup(struct sidtab *s,
> struct context *context,
> > u32 *index)
> > {
> > unsigned long flags;
> > - u32 count = (u32)atomic_read(&s->count);
> > + u32 count = (u32)refcount_read(&s->count);
> > u32 count_locked, level, pos;
> > struct sidtab_convert_params *convert;
> > struct context *dst, *dst_convert;
> > @@ -272,7 +272,7 @@ static int sidtab_reverse_lookup(struct sidtab *s,
> struct context *context,
> > spin_lock_irqsave(&s->lock, flags);
> >
> > convert = s->convert;
> > - count_locked = (u32)atomic_read(&s->count);
> > + count_locked = (u32)refcount_read(&s->count);
> > level = sidtab_level_from_count(count_locked);
> >
> > /* if count has changed before we acquired the lock, then catch up */
> > @@ -315,7 +315,7 @@ static int sidtab_reverse_lookup(struct sidtab *s,
> struct context *context,
> > }
> >
> > /* at this point we know the insert won't fail */
> > - atomic_set(&convert->target->count, count + 1);
> > + refcount_set(&convert->target->count, count + 1);
> > }
> >
> > if (context->len)
> > @@ -328,7 +328,7 @@ static int sidtab_reverse_lookup(struct sidtab *s,
> struct context *context,
> > /* write entries before writing new count */
> > smp_wmb();
> >
> > - atomic_set(&s->count, count + 1);
> > + refcount_set(&s->count, count + 1);
> >
> > rc = 0;
> > out_unlock:
> > @@ -418,7 +418,7 @@ int sidtab_convert(struct sidtab *s, struct
> sidtab_convert_params *params)
> > return -EBUSY;
> > }
> >
> > - count = (u32)atomic_read(&s->count);
> > + count = (u32)refcount_read(&s->count);
> > level = sidtab_level_from_count(count);
> >
> > /* allocate last leaf in the new sidtab (to avoid race with
> > @@ -431,7 +431,7 @@ int sidtab_convert(struct sidtab *s, struct
> sidtab_convert_params *params)
> > }
> >
> > /* set count in case no new entries are added during conversion */
> > - atomic_set(&params->target->count, count);
> > + refcount_set(&params->target->count, count);
> >
> > /* enable live convert of new entries */
> > s->convert = params;
> > diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h
> > index bbd5c0d1f3bd..68dd96a5beba 100644
> > --- a/security/selinux/ss/sidtab.h
> > +++ b/security/selinux/ss/sidtab.h
> > @@ -70,7 +70,7 @@ struct sidtab_convert_params {
> >
> > struct sidtab {
> > union sidtab_entry_inner roots[SIDTAB_MAX_LEVEL + 1];
> > - atomic_t count;
> > + refcount_t count;
> > struct sidtab_convert_params *convert;
> > spinlock_t lock;
> >
> > --
> > 2.17.1
> >
>
> Thanks,
>
> --
> Ondrej Mosnacek <omosnace at redhat dot com>
> Software Engineer, Security Technologies
> Red Hat, Inc.

2019-07-24 02:20:50

by Jann Horn

[permalink] [raw]
Subject: Re: [PATCH] selinux: convert struct sidtab count to refcount_t

On Mon, Jul 22, 2019 at 3:44 PM Ondrej Mosnacek <[email protected]> wrote:
> On Mon, Jul 22, 2019 at 1:35 PM NitinGote <[email protected]> wrote:
> > refcount_t type and corresponding API should be
> > used instead of atomic_t when the variable is used as
> > a reference counter. This allows to avoid accidental
> > refcounter overflows that might lead to use-after-free
> > situations.
> >
> > Signed-off-by: NitinGote <[email protected]>
>
> Nack.
>
> The 'count' variable is not used as a reference counter here. It
> tracks the number of entries in sidtab, which is a very specific
> lookup table that can only grow (the count never decreases). I only
> made it atomic because the variable is read outside of the sidtab's
> spin lock and thus the reads and writes to it need to be guaranteed to
> be atomic. The counter is only updated under the spin lock, so
> insertions do not race with each other.

Probably shouldn't even be atomic_t... quoting Documentation/atomic_t.txt:

| SEMANTICS
| ---------
|
| Non-RMW ops:
|
| The non-RMW ops are (typically) regular LOADs and STOREs and are canonically
| implemented using READ_ONCE(), WRITE_ONCE(), smp_load_acquire() and
| smp_store_release() respectively. Therefore, if you find yourself only using
| the Non-RMW operations of atomic_t, you do not in fact need atomic_t at all
| and are doing it wrong.

So I think what you actually want here is a plain "int count", and then:
- for unlocked reads, either READ_ONCE()+smp_rmb() or smp_load_acquire()
- for writes, either smp_wmb()+WRITE_ONCE() or smp_store_release()

smp_load_acquire() and smp_store_release() are probably the nicest
here, since they are semantically clearer than smp_rmb()/smp_wmb().

2019-07-24 02:33:25

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH] selinux: convert struct sidtab count to refcount_t

On Tue, Jul 23, 2019 at 04:53:47PM +0200, Jann Horn wrote:
> On Mon, Jul 22, 2019 at 3:44 PM Ondrej Mosnacek <[email protected]> wrote:
> > On Mon, Jul 22, 2019 at 1:35 PM NitinGote <[email protected]> wrote:
> > > refcount_t type and corresponding API should be
> > > used instead of atomic_t when the variable is used as
> > > a reference counter. This allows to avoid accidental
> > > refcounter overflows that might lead to use-after-free
> > > situations.
> > >
> > > Signed-off-by: NitinGote <[email protected]>
> >
> > Nack.
> >
> > The 'count' variable is not used as a reference counter here. It
> > tracks the number of entries in sidtab, which is a very specific
> > lookup table that can only grow (the count never decreases). I only
> > made it atomic because the variable is read outside of the sidtab's
> > spin lock and thus the reads and writes to it need to be guaranteed to
> > be atomic. The counter is only updated under the spin lock, so
> > insertions do not race with each other.
>
> Probably shouldn't even be atomic_t... quoting Documentation/atomic_t.txt:
>
> | SEMANTICS
> | ---------
> |
> | Non-RMW ops:
> |
> | The non-RMW ops are (typically) regular LOADs and STOREs and are canonically
> | implemented using READ_ONCE(), WRITE_ONCE(), smp_load_acquire() and
> | smp_store_release() respectively. Therefore, if you find yourself only using
> | the Non-RMW operations of atomic_t, you do not in fact need atomic_t at all
> | and are doing it wrong.
>
> So I think what you actually want here is a plain "int count", and then:
> - for unlocked reads, either READ_ONCE()+smp_rmb() or smp_load_acquire()
> - for writes, either smp_wmb()+WRITE_ONCE() or smp_store_release()
>
> smp_load_acquire() and smp_store_release() are probably the nicest
> here, since they are semantically clearer than smp_rmb()/smp_wmb().

Perhaps we need a "statistics" counter type for these kinds of counters?
"counter_t"? I bet there are a lot of atomic_t uses that are just trying
to be counters. (likely most of atomic_t that isn't now refcount_t ...)

--
Kees Cook

2019-07-24 14:31:35

by Jann Horn

[permalink] [raw]
Subject: Re: [PATCH] selinux: convert struct sidtab count to refcount_t

On Wed, Jul 24, 2019 at 12:17 AM Kees Cook <[email protected]> wrote:
> On Tue, Jul 23, 2019 at 04:53:47PM +0200, Jann Horn wrote:
> > On Mon, Jul 22, 2019 at 3:44 PM Ondrej Mosnacek <[email protected]> wrote:
> > > On Mon, Jul 22, 2019 at 1:35 PM NitinGote <[email protected]> wrote:
> > > > refcount_t type and corresponding API should be
> > > > used instead of atomic_t when the variable is used as
> > > > a reference counter. This allows to avoid accidental
> > > > refcounter overflows that might lead to use-after-free
> > > > situations.
> > > >
> > > > Signed-off-by: NitinGote <[email protected]>
> > >
> > > Nack.
> > >
> > > The 'count' variable is not used as a reference counter here. It
> > > tracks the number of entries in sidtab, which is a very specific
> > > lookup table that can only grow (the count never decreases). I only
> > > made it atomic because the variable is read outside of the sidtab's
> > > spin lock and thus the reads and writes to it need to be guaranteed to
> > > be atomic. The counter is only updated under the spin lock, so
> > > insertions do not race with each other.
> >
> > Probably shouldn't even be atomic_t... quoting Documentation/atomic_t.txt:
> >
> > | SEMANTICS
> > | ---------
> > |
> > | Non-RMW ops:
> > |
> > | The non-RMW ops are (typically) regular LOADs and STOREs and are canonically
> > | implemented using READ_ONCE(), WRITE_ONCE(), smp_load_acquire() and
> > | smp_store_release() respectively. Therefore, if you find yourself only using
> > | the Non-RMW operations of atomic_t, you do not in fact need atomic_t at all
> > | and are doing it wrong.
> >
> > So I think what you actually want here is a plain "int count", and then:
> > - for unlocked reads, either READ_ONCE()+smp_rmb() or smp_load_acquire()
> > - for writes, either smp_wmb()+WRITE_ONCE() or smp_store_release()
> >
> > smp_load_acquire() and smp_store_release() are probably the nicest
> > here, since they are semantically clearer than smp_rmb()/smp_wmb().
>
> Perhaps we need a "statistics" counter type for these kinds of counters?
> "counter_t"? I bet there are a lot of atomic_t uses that are just trying
> to be counters. (likely most of atomic_t that isn't now refcount_t ...)

This isn't a statistics counter though; this thing needs ordered
memory accesses, which you wouldn't need for statistics.

2019-07-24 16:59:10

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH] selinux: convert struct sidtab count to refcount_t

On Wed, Jul 24, 2019 at 04:28:31PM +0200, Jann Horn wrote:
> On Wed, Jul 24, 2019 at 12:17 AM Kees Cook <[email protected]> wrote:
> > Perhaps we need a "statistics" counter type for these kinds of counters?
> > "counter_t"? I bet there are a lot of atomic_t uses that are just trying
> > to be counters. (likely most of atomic_t that isn't now refcount_t ...)
>
> This isn't a statistics counter though; this thing needs ordered
> memory accesses, which you wouldn't need for statistics.

Okay, it'd be a "very accurate" counter type? It _could_ be used for
statistics. I guess what I mean is that there are a lot of places using
atomic_t just for upward counting that don't care about wrapping, etc.

--
Kees Cook

2019-07-24 17:01:01

by Ondrej Mosnacek

[permalink] [raw]
Subject: Re: [PATCH] selinux: convert struct sidtab count to refcount_t

On Tue, Jul 23, 2019 at 4:54 PM Jann Horn <[email protected]> wrote:
> On Mon, Jul 22, 2019 at 3:44 PM Ondrej Mosnacek <[email protected]> wrote:
> > On Mon, Jul 22, 2019 at 1:35 PM NitinGote <[email protected]> wrote:
> > > refcount_t type and corresponding API should be
> > > used instead of atomic_t when the variable is used as
> > > a reference counter. This allows to avoid accidental
> > > refcounter overflows that might lead to use-after-free
> > > situations.
> > >
> > > Signed-off-by: NitinGote <[email protected]>
> >
> > Nack.
> >
> > The 'count' variable is not used as a reference counter here. It
> > tracks the number of entries in sidtab, which is a very specific
> > lookup table that can only grow (the count never decreases). I only
> > made it atomic because the variable is read outside of the sidtab's
> > spin lock and thus the reads and writes to it need to be guaranteed to
> > be atomic. The counter is only updated under the spin lock, so
> > insertions do not race with each other.
>
> Probably shouldn't even be atomic_t... quoting Documentation/atomic_t.txt:
>
> | SEMANTICS
> | ---------
> |
> | Non-RMW ops:
> |
> | The non-RMW ops are (typically) regular LOADs and STOREs and are canonically
> | implemented using READ_ONCE(), WRITE_ONCE(), smp_load_acquire() and
> | smp_store_release() respectively. Therefore, if you find yourself only using
> | the Non-RMW operations of atomic_t, you do not in fact need atomic_t at all
> | and are doing it wrong.
>
> So I think what you actually want here is a plain "int count", and then:
> - for unlocked reads, either READ_ONCE()+smp_rmb() or smp_load_acquire()
> - for writes, either smp_wmb()+WRITE_ONCE() or smp_store_release()
>
> smp_load_acquire() and smp_store_release() are probably the nicest
> here, since they are semantically clearer than smp_rmb()/smp_wmb().

Oh yes, I had a hunch that there would be a better way to do it... I
should have taken the time to read the documentation carefully :)

I am on PTO today, but I will be happy to send a patch to convert the
atomic_t usage to the smp_load_acquire()/smp_store_release() helpers
tomorrow. It will also allow us to just use u32 directly and to get
rid of the ugly casts and the INT_MAX limit.

Thanks a lot for the hint, Jann!

--
Ondrej Mosnacek <omosnace at redhat dot com>
Software Engineer, Security Technologies
Red Hat, Inc.

2019-07-24 17:09:43

by Jann Horn

[permalink] [raw]
Subject: Re: [PATCH] selinux: convert struct sidtab count to refcount_t

On Wed, Jul 24, 2019 at 5:54 PM Kees Cook <[email protected]> wrote:
> On Wed, Jul 24, 2019 at 04:28:31PM +0200, Jann Horn wrote:
> > On Wed, Jul 24, 2019 at 12:17 AM Kees Cook <[email protected]> wrote:
> > > Perhaps we need a "statistics" counter type for these kinds of counters?
> > > "counter_t"? I bet there are a lot of atomic_t uses that are just trying
> > > to be counters. (likely most of atomic_t that isn't now refcount_t ...)
> >
> > This isn't a statistics counter though; this thing needs ordered
> > memory accesses, which you wouldn't need for statistics.
>
> Okay, it'd be a "very accurate" counter type? It _could_ be used for
> statistics. I guess what I mean is that there are a lot of places using
> atomic_t just for upward counting that don't care about wrapping, etc.

(Accurate) statistics counters need RMW ops, don't need memory
ordering, usually can't be locked against writes, and may not care
about wrapping.
This thing doesn't need RMW ops, does need memory ordering, can be
locked against writes, and definitely shouldn't wrap.

I agree that there are a bunch of statistics counters in the kernel,
and it's not necessarily a bad idea to use a separate type for them;
but this is not a statistics counter.

2019-07-29 17:04:28

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH] selinux: convert struct sidtab count to refcount_t

On Wed, Jul 24, 2019 at 06:55:47PM +0200, Jann Horn wrote:
> (Accurate) statistics counters need RMW ops, don't need memory
> ordering, usually can't be locked against writes, and may not care
> about wrapping.
> This thing doesn't need RMW ops, does need memory ordering, can be
> locked against writes, and definitely shouldn't wrap.
>
> I agree that there are a bunch of statistics counters in the kernel,
> and it's not necessarily a bad idea to use a separate type for them;
> but this is not a statistics counter.

Right, yes, I didn't meant to suggest it should be. I was just bringing
up the "counter type" idea again, since it was on my mind here
originally.

--
Kees Cook