From: James Carter <jwcart2@gmail.com>
To: "Christian Göttsche" <cgzones@googlemail.com>
Cc: selinux@vger.kernel.org
Subject: Re: [PATCH v2] checkpolicy, libsepol: Fix potential double free of mls_level_t
Date: Wed, 28 Feb 2024 08:48:52 -0500 [thread overview]
Message-ID: <CAP+JOzT8qg5CEE5YJ65yajdLsvRd9HNQYMQkxY040qpqVr+CYg@mail.gmail.com> (raw)
In-Reply-To: <CAJ2a_DcNB7nd2OejzukWiWKa+zpCitXzx1GMs0Ei0dszZYUG4Q@mail.gmail.com>
On Mon, Feb 26, 2024 at 11:33 AM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> On Wed, 21 Feb 2024 at 22:08, James Carter <jwcart2@gmail.com> wrote:
> >
> > In checkpolicy, a sensitivity that has one or more aliases will
> > temporarily share the mls_level_t structure with its aliases until
> > a level statement is processed for the sensitivity (or one of the
> > aliases) and the aliases are updated to have their own mls_level_t
> > structure. If the policydb is destroyed while they are sharing the
> > mls_level_t structure, then a double free of the shared mls_level_t
> > will occur. This does not currently occur only because checkpolicy
> > does very little clean-up before exiting.
> >
> > The "defined" field of the level_datum_t is set after a level
> > statement is processed for a sensitivity and its aliases. This means
> > that we know an alias has its own mls_level_t if the "defined" field
> > is set. The double free can be avoided by not destroying the
> > mls_leve_t structure for an alias unless the "defined" field is set.
> >
> > Since the "defined" field is only set to false while the mls_level_t
> > structure is being shared, it would be clearer to rename the field
> > as "notdefined". It would only be set during the time the sensitivity
> > and its aliases are sharing the mls_level_t structure. Outside of
> > checkpolicy, the "notdefined" field will always be set to 0.
> >
> > Signed-off-by: James Carter <jwcart2@gmail.com>
> > ---
> > v2: Change the field name from "defined" to "notdefined" and change
> > the logic to match.
>
> Thanks, in my opinion this is a much nicer approach.
>
> Maybe check in libsepol/src/policydb_validate.c:validate_level_datum()
> that notdefined is FALSE?
>
That is a good idea.
> > checkpolicy/checkpolicy.c | 7 +++----
> > checkpolicy/policy_define.c | 10 ++++++----
> > libsepol/cil/src/cil_binary.c | 3 ---
> > libsepol/include/sepol/policydb/policydb.h | 2 +-
> > libsepol/src/policydb.c | 6 ++++--
> > 5 files changed, 14 insertions(+), 14 deletions(-)
> >
> > diff --git a/checkpolicy/checkpolicy.c b/checkpolicy/checkpolicy.c
> > index fcec6e51..d7cafaa4 100644
> > --- a/checkpolicy/checkpolicy.c
> > +++ b/checkpolicy/checkpolicy.c
> > @@ -370,10 +370,9 @@ static int check_level(hashtab_key_t key, hashtab_datum_t datum, void *arg __att
> > {
> > level_datum_t *levdatum = (level_datum_t *) datum;
> >
> > - if (!levdatum->isalias && !levdatum->defined) {
> > - fprintf(stderr,
> > - "Error: sensitivity %s was not used in a level definition!\n",
> > - key);
> > + if (!levdatum->isalias && levdatum->notdefined) {
> > + fprintf(stderr, "Error: sensitivity %s was not used in a level definition!\n",
> > + key);
> > return -1;
> > }
> > return 0;
> > diff --git a/checkpolicy/policy_define.c b/checkpolicy/policy_define.c
> > index 260e609d..ac215086 100644
> > --- a/checkpolicy/policy_define.c
> > +++ b/checkpolicy/policy_define.c
> > @@ -743,6 +743,7 @@ int define_sens(void)
> > level_datum_init(datum);
> > datum->isalias = FALSE;
> > datum->level = level;
> > + datum->notdefined = TRUE;
> >
> > ret = declare_symbol(SYM_LEVELS, id, datum, &value, &value);
> > switch (ret) {
> > @@ -780,6 +781,7 @@ int define_sens(void)
> > level_datum_init(aliasdatum);
> > aliasdatum->isalias = TRUE;
> > aliasdatum->level = level;
> > + aliasdatum->notdefined = TRUE;
> >
> > ret = declare_symbol(SYM_LEVELS, id, aliasdatum, NULL, &value);
> > switch (ret) {
> > @@ -1006,9 +1008,10 @@ static int clone_level(hashtab_key_t key __attribute__ ((unused)), hashtab_datum
> > mls_level_t *level = (mls_level_t *) arg, *newlevel;
> >
> > if (levdatum->level == level) {
> > - levdatum->defined = 1;
> > - if (!levdatum->isalias)
> > + if (!levdatum->isalias) {
> > + levdatum->notdefined = FALSE;
> > return 0;
> > + }
> > newlevel = (mls_level_t *) malloc(sizeof(mls_level_t));
> > if (!newlevel)
> > return -1;
> > @@ -1017,6 +1020,7 @@ static int clone_level(hashtab_key_t key __attribute__ ((unused)), hashtab_datum
> > return -1;
> > }
> > levdatum->level = newlevel;
> > + levdatum->notdefined = FALSE;
> > }
> > return 0;
> > }
> > @@ -1057,8 +1061,6 @@ int define_level(void)
> > }
> > free(id);
> >
> > - levdatum->defined = 1;
> > -
> > while ((id = queue_remove(id_queue))) {
> > cat_datum_t *cdatum;
> > int range_start, range_end, i;
> > diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
> > index a8e3616a..95bd18ba 100644
> > --- a/libsepol/cil/src/cil_binary.c
> > +++ b/libsepol/cil/src/cil_binary.c
> > @@ -907,7 +907,6 @@ static int cil_sensalias_to_policydb(policydb_t *pdb, struct cil_alias *cil_alia
> > goto exit;
> > }
> > sepol_alias->level = mls_level;
> > - sepol_alias->defined = 1;
> > sepol_alias->isalias = 1;
> >
> > return SEPOL_OK;
> > @@ -3163,8 +3162,6 @@ int cil_sepol_level_define(policydb_t *pdb, struct cil_sens *cil_sens)
> > }
> > }
> >
> > - sepol_level->defined = 1;
> > -
> > return SEPOL_OK;
> >
> > exit:
> > diff --git a/libsepol/include/sepol/policydb/policydb.h b/libsepol/include/sepol/policydb/policydb.h
> > index 6682069e..66d93999 100644
> > --- a/libsepol/include/sepol/policydb/policydb.h
> > +++ b/libsepol/include/sepol/policydb/policydb.h
> > @@ -217,7 +217,7 @@ typedef struct user_datum {
> > typedef struct level_datum {
> > mls_level_t *level; /* sensitivity and associated categories */
> > unsigned char isalias; /* is this sensitivity an alias for another? */
> > - unsigned char defined;
> > + unsigned char notdefined;
>
> Maybe add a small comment that it's only used as an optimization in
> checkpolicy and is 0 for fully parsed or generated policies?
>
Also a good idea.
Thanks,
Jim
> > } level_datum_t;
> >
> > /* Category attributes */
> > diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
> > index f10a8a95..0c23a7a2 100644
> > --- a/libsepol/src/policydb.c
> > +++ b/libsepol/src/policydb.c
> > @@ -1390,8 +1390,10 @@ static int sens_destroy(hashtab_key_t key, hashtab_datum_t datum, void *p
> > if (key)
> > free(key);
> > levdatum = (level_datum_t *) datum;
> > - mls_level_destroy(levdatum->level);
> > - free(levdatum->level);
> > + if (!levdatum->isalias || !levdatum->notdefined) {
> > + mls_level_destroy(levdatum->level);
> > + free(levdatum->level);
> > + }
> > level_datum_destroy(levdatum);
> > free(levdatum);
> > return 0;
> > --
> > 2.43.0
> >
prev parent reply other threads:[~2024-02-28 13:49 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-02-21 21:07 [PATCH v2] checkpolicy, libsepol: Fix potential double free of mls_level_t James Carter
2024-02-26 16:33 ` Christian Göttsche
2024-02-28 13:48 ` James Carter [this message]
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=CAP+JOzT8qg5CEE5YJ65yajdLsvRd9HNQYMQkxY040qpqVr+CYg@mail.gmail.com \
--to=jwcart2@gmail.com \
--cc=cgzones@googlemail.com \
--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 read-only IMAP folder(s) and NNTP newsgroup(s).