All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies
@ 2021-10-11 16:24 Christian Göttsche
  2021-10-11 16:24 ` [RFC PATCH 01/35] cifuzz: enable report-unreproducible-crashes Christian Göttsche
                   ` (36 more replies)
  0 siblings, 37 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:24 UTC (permalink / raw)
  To: selinux

Add a libfuzz[1] based fuzzer testing the reading and parsing of binary policy
files. This fuzzer will be run within the OSS-Fuzz service.

Handle and reject a variety of edge cases causing crashes or resource leaks.

The fifth patch ("libsepol/fuzz: limit element sizes for fuzzing") needs some
discussion: To avoid oom reports from the fuzzer, caused by huge memory
allocations, all identifiers are limited to a length of 2^16 for the fuzzer
build only.  Probably there should be a limit for the release build too.
Is there a specification for the binary policy format saying something about
the maximum length of identifiers?
After a quick look at the kernel sources (most interesting is str_read()) I
could not find any limits either.

[1]: https://llvm.org/docs/LibFuzzer.html

Christian Göttsche (35):
  cifuzz: enable report-unreproducible-crashes
  cifuzz: use the default runtime of 600 seconds
  libsepol/fuzz: silence secilc-fuzzer
  libsepol: add libfuzz based fuzzer for reading binary policies
  libsepol/fuzz: limit element sizes for fuzzing
  libsepol: use logging framework in conditional.c
  libsepol: use logging framework in ebitmap.c
  libsepol: use mallocarray wrapper to avoid overflows
  libsepol: use reallocarray wrapper to avoid overflows
  libsepol: add checks for read sizes
  libsepol: enforce avtab item limit
  libsepol: clean memory on conditional read failure
  libsepol: validate MLS levels
  libsepol: reject invalid fsuse types
  libsepol: reject invalid default targets
  libsepol: validate expanded user range and level
  libsepol: validate types
  libsepol: use size_t for indexes in strs helpers
  libsepol: reject abnormal huge sid ids
  libsepol: do not crash on class gaps
  libsepol: do not crash on user gaps
  libsepol: validate permission count of classes
  libsepol: resolve log message mismatch
  libsepol: zero member before potential dereference
  libsepol: validate avtab types
  libsepol: validate constraint expression operators and attributes
  libsepol: validate type of avtab type rules
  libsepol: validate ocontexts
  libsepol: validate genfs contexts
  libsepol: validate permissive types
  libsepol: validate policy properties
  libsepol: do not underflow on short format arguments
  libsepol: validate categories
  libsepol: use correct size for initial string list
  libsepol: do not create a string list with initial size zero

 .github/workflows/cifuzz.yml     |   3 +-
 libsepol/fuzz/binpolicy-fuzzer.c |  63 +++++++
 libsepol/fuzz/policy.bin         | Bin 0 -> 1552 bytes
 libsepol/fuzz/secilc-fuzzer.c    |   5 +
 libsepol/src/Makefile            |   6 +
 libsepol/src/avtab.c             |   6 +
 libsepol/src/conditional.c       |  36 ++--
 libsepol/src/ebitmap.c           |  27 ++-
 libsepol/src/expand.c            |   4 +-
 libsepol/src/hashtab.c           |   4 +-
 libsepol/src/kernel_to_cil.c     |  10 ++
 libsepol/src/kernel_to_common.c  |  23 ++-
 libsepol/src/kernel_to_common.h  |   4 +-
 libsepol/src/kernel_to_conf.c    |  13 +-
 libsepol/src/link.c              |   3 +-
 libsepol/src/module.c            |   4 +-
 libsepol/src/module_to_cil.c     |  13 +-
 libsepol/src/optimize.c          |  11 +-
 libsepol/src/policydb.c          |  68 +++++++-
 libsepol/src/policydb_validate.c | 274 +++++++++++++++++++++++++++++--
 libsepol/src/private.h           |  27 ++-
 libsepol/src/services.c          |  12 +-
 libsepol/src/sidtab.c            |   3 +-
 libsepol/src/user_record.c       |   8 +-
 libsepol/src/users.c             |  12 +-
 libsepol/src/util.c              |  11 +-
 libsepol/src/write.c             |   2 +-
 scripts/oss-fuzz.sh              |  19 ++-
 28 files changed, 556 insertions(+), 115 deletions(-)
 create mode 100644 libsepol/fuzz/binpolicy-fuzzer.c
 create mode 100644 libsepol/fuzz/policy.bin

-- 
2.33.0


^ permalink raw reply	[flat|nested] 135+ messages in thread

* [RFC PATCH 01/35] cifuzz: enable report-unreproducible-crashes
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
@ 2021-10-11 16:24 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 02/35] cifuzz: use the default runtime of 600 seconds Christian Göttsche
                   ` (35 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:24 UTC (permalink / raw)
  To: selinux

Fail and report unreproducible fuzzing crashes and leaks. Such failures
are probably related to some global state not properly reset in the
fuzzer and can cause OSS-Fuzz to report flaky issues.

Suggested-by: Evgeny Vereshchagin
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 .github/workflows/cifuzz.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
index 5c2233a2..b28eb71a 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/cifuzz.yml
@@ -30,6 +30,7 @@ jobs:
           oss-fuzz-project-name: 'selinux'
           fuzz-seconds: 180
           dry-run: false
+          report-unreproducible-crashes: true
           sanitizer: ${{ matrix.sanitizer }}
       - name: Upload Crash
         uses: actions/upload-artifact@v1
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 02/35] cifuzz: use the default runtime of 600 seconds
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
  2021-10-11 16:24 ` [RFC PATCH 01/35] cifuzz: enable report-unreproducible-crashes Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 03/35] libsepol/fuzz: silence secilc-fuzzer Christian Göttsche
                   ` (34 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

The default runtime for CIFuzz is 600 seconds; use it.

Since GitHub pull-requests are not the main contribution workflow the
number of runs should be manageable.

[1]: https://google.github.io/oss-fuzz/getting-started/continuous-integration/

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 .github/workflows/cifuzz.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
index b28eb71a..92523db4 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/cifuzz.yml
@@ -28,7 +28,7 @@ jobs:
         uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
         with:
           oss-fuzz-project-name: 'selinux'
-          fuzz-seconds: 180
+          fuzz-seconds: 600
           dry-run: false
           report-unreproducible-crashes: true
           sanitizer: ${{ matrix.sanitizer }}
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 03/35] libsepol/fuzz: silence secilc-fuzzer
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
  2021-10-11 16:24 ` [RFC PATCH 01/35] cifuzz: enable report-unreproducible-crashes Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 02/35] cifuzz: use the default runtime of 600 seconds Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 04/35] libsepol: add libfuzz based fuzzer for reading binary policies Christian Göttsche
                   ` (33 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Do not output CIL log messages while fuzzing, since their amount are
huge, e.g. for neverallow or typebounds violations.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/fuzz/secilc-fuzzer.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libsepol/fuzz/secilc-fuzzer.c b/libsepol/fuzz/secilc-fuzzer.c
index 255b3241..9a1a16de 100644
--- a/libsepol/fuzz/secilc-fuzzer.c
+++ b/libsepol/fuzz/secilc-fuzzer.c
@@ -8,6 +8,10 @@
 #include <sepol/cil/cil.h>
 #include <sepol/policydb.h>
 
+static void log_handler(__attribute__((unused)) int lvl, __attribute__((unused)) const char *msg) {
+	/* be quiet */
+}
+
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
 	enum cil_log_level log_level = CIL_ERR;
 	struct sepol_policy_file *pf = NULL;
@@ -24,6 +28,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
 	sepol_policydb_t *pdb = NULL;
 
 	cil_set_log_level(log_level);
+	cil_set_log_handler(log_handler);
 
 	cil_db_init(&db);
 	cil_set_disable_dontaudit(db, disable_dontaudit);
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 04/35] libsepol: add libfuzz based fuzzer for reading binary policies
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (2 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 03/35] libsepol/fuzz: silence secilc-fuzzer Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 05/35] libsepol/fuzz: limit element sizes for fuzzing Christian Göttsche
                   ` (32 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Introduce a libfuzz[1] based fuzzer testing the parsing of a binary
policy.

Build the fuzzer in the oss-fuzz script.

[1]: https://llvm.org/docs/LibFuzzer.html

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/fuzz/binpolicy-fuzzer.c |  63 +++++++++++++++++++++++++++++++
 libsepol/fuzz/policy.bin         | Bin 0 -> 1552 bytes
 scripts/oss-fuzz.sh              |  19 ++++++++--
 3 files changed, 79 insertions(+), 3 deletions(-)
 create mode 100644 libsepol/fuzz/binpolicy-fuzzer.c
 create mode 100644 libsepol/fuzz/policy.bin

diff --git a/libsepol/fuzz/binpolicy-fuzzer.c b/libsepol/fuzz/binpolicy-fuzzer.c
new file mode 100644
index 00000000..85c59645
--- /dev/null
+++ b/libsepol/fuzz/binpolicy-fuzzer.c
@@ -0,0 +1,63 @@
+#include <sepol/debug.h>
+#include <sepol/kernel_to_cil.h>
+#include <sepol/kernel_to_conf.h>
+#include <sepol/policydb/policydb.h>
+
+extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static int write_binary_policy(policydb_t *p, FILE *outfp)
+{
+	struct policy_file pf;
+
+	policy_file_init(&pf);
+	pf.type = PF_USE_STDIO;
+	pf.fp = outfp;
+	return policydb_write(p, &pf);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+	policydb_t policydb = {};
+	sidtab_t sidtab = {};
+	struct policy_file pf;
+	FILE *devnull = NULL;
+
+	sepol_debug(0);
+
+	policy_file_init(&pf);
+	pf.type = PF_USE_MEMORY;
+	pf.data = (char *) data;
+	pf.len = size;
+
+	if (policydb_init(&policydb))
+		goto exit;
+
+	if (policydb_read(&policydb, &pf, /*verbose=*/0))
+		goto exit;
+
+	if (policydb_load_isids(&policydb, &sidtab))
+		goto exit;
+
+	if (policydb.policy_type == POLICY_KERN)
+		(void) policydb_optimize(&policydb);
+
+	devnull = fopen("/dev/null", "w");
+	if (!devnull)
+		goto exit;
+
+	(void) write_binary_policy(&policydb, devnull);
+
+	(void) sepol_kernel_policydb_to_conf(devnull, &policydb);
+
+	(void) sepol_kernel_policydb_to_cil(devnull, &policydb);
+
+exit:
+	if (devnull != NULL)
+		fclose(devnull);
+
+	policydb_destroy(&policydb);
+	sepol_sidtab_destroy(&sidtab);
+
+	/* Non-zero return values are reserved for future use. */
+	return 0;
+}
diff --git a/libsepol/fuzz/policy.bin b/libsepol/fuzz/policy.bin
new file mode 100644
index 0000000000000000000000000000000000000000..6f977ef34479daa9bf2e848c502ecea8d96f7912
GIT binary patch
literal 1552
zcma)5OLBuS3?==4PtZ+{&?9)$U3WbIlYnX65X0D})6Db;y>M5p9{5ov4Nx%;$<mW$
z3H<r}@pX|T$<xE~(b(pFDfU7D-=#naD2m2Fg9jW(-^m~bGdGSNY)e53<fv2qdtGkQ
z!jzhhLpdx(PWIwvbIwVQy0qhU$VF|O4}e{}D%0NI#$~><!L6(}!BqAt@_s$c!a#sw
zC$j7XLx!Aos(%-zs7Bl3l+Sv4XN--GML2e*`6?Tq1A9jjY>40a)K#TcVgu}o@qItz
z*n@Vpe$`n>9k>)lLo|5!#vC+5dA)f~edbIZ(r^=r47z&T$5@O7acJXBjy1qIauI91
zZV#hm%^Wra%{;^@N(_Mfp@x57&=A0V{XH^N#4uZ2$+ZB!To<dR3|?FRA3At3Wr~g%
zz~016vi3X+@#ERQVqoAOx)TgDxswt<g<podAL6jTDGsjGTrHewj)RLe$3eey9G;aL
td_V~(^i6Tdr3M$kUC#Ae9okPlwF6@KhlL%sbur5q>K{?!0dQgn^$*KOS$hBg

literal 0
HcmV?d00001

diff --git a/scripts/oss-fuzz.sh b/scripts/oss-fuzz.sh
index 16cc3c0a..14bad14d 100755
--- a/scripts/oss-fuzz.sh
+++ b/scripts/oss-fuzz.sh
@@ -32,10 +32,10 @@ SANITIZER=${SANITIZER:-address}
 flags="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=$SANITIZER -fsanitize=fuzzer-no-link"
 
 export CC=${CC:-clang}
-export CFLAGS=${CFLAGS:-$flags}
+export CFLAGS="${CFLAGS:-$flags} -I$DESTDIR/usr/include -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
 
 export CXX=${CXX:-clang++}
-export CXXFLAGS=${CXXFLAGS:-$flags}
+export CXXFLAGS="${CXXFLAGS:-$flags}"
 
 export OUT=${OUT:-$(pwd)/out}
 mkdir -p "$OUT"
@@ -49,11 +49,24 @@ make -C libsepol clean
 # shellcheck disable=SC2016
 make -C libsepol V=1 LD_SONAME_FLAGS='-soname,$(LIBSO),--version-script=$(LIBMAP)' -j"$(nproc)" install
 
+## secilc fuzzer ##
+
 # CFLAGS, CXXFLAGS and LIB_FUZZING_ENGINE have to be split to be accepted by
 # the compiler/linker so they shouldn't be quoted
 # shellcheck disable=SC2086
-$CC $CFLAGS -I"$DESTDIR/usr/include" -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -c -o secilc-fuzzer.o libsepol/fuzz/secilc-fuzzer.c
+$CC $CFLAGS -c -o secilc-fuzzer.o libsepol/fuzz/secilc-fuzzer.c
 # shellcheck disable=SC2086
 $CXX $CXXFLAGS $LIB_FUZZING_ENGINE secilc-fuzzer.o "$DESTDIR/usr/lib/libsepol.a" -o "$OUT/secilc-fuzzer"
 
 zip -r "$OUT/secilc-fuzzer_seed_corpus.zip" secilc/test
+
+## binary policy fuzzer ##
+
+# CFLAGS, CXXFLAGS and LIB_FUZZING_ENGINE have to be split to be accepted by
+# the compiler/linker so they shouldn't be quoted
+# shellcheck disable=SC2086
+$CC $CFLAGS -c -o binpolicy-fuzzer.o libsepol/fuzz/binpolicy-fuzzer.c
+# shellcheck disable=SC2086
+$CXX $CXXFLAGS $LIB_FUZZING_ENGINE binpolicy-fuzzer.o "$DESTDIR/usr/lib/libsepol.a" -o "$OUT/binpolicy-fuzzer"
+
+zip -j "$OUT/binpolicy-fuzzer_seed_corpus.zip" libsepol/fuzz/policy.bin
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 05/35] libsepol/fuzz: limit element sizes for fuzzing
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (3 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 04/35] libsepol: add libfuzz based fuzzer for reading binary policies Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 06/35] libsepol: use logging framework in conditional.c Christian Göttsche
                   ` (31 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Limit the maximum length of read sizes, like string length of module
version and name or keys and number of symtab entries.  This avoids the
fuzzer to report oom events for huge allocations (it also improves the
number of executions per seconds of the fuzzer).

This change only affects the fuzzer build.

    ==15211== ERROR: libFuzzer: out-of-memory (malloc(3115956666))
       To change the out-of-memory limit use -rss_limit_mb=<N>

        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aa143 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa143)
        #7 0x5259cb in malloc (./out/binpolicy-fuzzer+0x5259cb)
        #8 0x59d307 in str_read ./libsepol/src/services.c:1746:8
        #9 0x585b97 in perm_read ./libsepol/src/policydb.c:2063:5
        #10 0x581f8a in common_read ./libsepol/src/policydb.c:2119:7
        #11 0x576681 in policydb_read ./libsepol/src/policydb.c:4417:8
        #12 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
        #13 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #14 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #15 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #16 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #17 0x7fe1ec88a7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #18 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

    ==13584== ERROR: libFuzzer: out-of-memory (malloc(2560137369))
       To change the out-of-memory limit use -rss_limit_mb=<N>

        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aa143 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa143)
        #7 0x5259cb in malloc (./out/binpolicy-fuzzer+0x5259cb)
        #8 0x581cc4 in common_read ./libsepol/src/policydb.c:2108:8
        #9 0x576681 in policydb_read ./libsepol/src/policydb.c:4409:8
        #10 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
        #11 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #12 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #13 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #14 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #15 0x7fa6431787ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #16 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

    ==12683== ERROR: libFuzzer: out-of-memory (malloc(2526451450))
       To change the out-of-memory limit use -rss_limit_mb=<N>

        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aa143 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa143)
        #7 0x5259cb in malloc (./out/binpolicy-fuzzer+0x5259cb)
        #8 0x575f8a in policydb_read ./libsepol/src/policydb.c:4356:18
        #9 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
        #10 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #11 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #12 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #13 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #14 0x7fa737b377ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #15 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/private.h | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/libsepol/src/private.h b/libsepol/src/private.h
index 71287282..6146f59f 100644
--- a/libsepol/src/private.h
+++ b/libsepol/src/private.h
@@ -44,7 +44,12 @@
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
 
-#define is_saturated(x) (x == (typeof(x))-1)
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+# define is_saturated(x) (x == (typeof(x))-1 || (x) > (1U << 16))
+#else
+# define is_saturated(x) (x == (typeof(x))-1)
+#endif
+
 #define zero_or_saturated(x) ((x == 0) || is_saturated(x))
 
 #define spaceship_cmp(a, b) (((a) > (b)) - ((a) < (b)))
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 06/35] libsepol: use logging framework in conditional.c
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (4 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 05/35] libsepol/fuzz: limit element sizes for fuzzing Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 07/35] libsepol: use logging framework in ebitmap.c Christian Göttsche
                   ` (30 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Use the internal logging framework instead of directly writing to
stdout as it might be undesired to do so within a library.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/conditional.c | 30 +++++++++++-------------------
 1 file changed, 11 insertions(+), 19 deletions(-)

diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c
index e3ede694..a01350a6 100644
--- a/libsepol/src/conditional.c
+++ b/libsepol/src/conditional.c
@@ -25,6 +25,7 @@
 #include <sepol/policydb/conditional.h>
 
 #include "private.h"
+#include "debug.h"
 
 /* move all type rules to top of t/f lists to help kernel on evaluation */
 static void cond_optimize(cond_av_list_t ** l)
@@ -314,8 +315,7 @@ static int evaluate_cond_node(policydb_t * p, cond_node_t * node)
 	if (new_state != node->cur_state) {
 		node->cur_state = new_state;
 		if (new_state == -1)
-			printf
-			    ("expression result was undefined - disabling all rules.\n");
+			WARN(NULL, "expression result was undefined - disabling all rules.\n");
 		/* turn the rules on or off */
 		for (cur = node->true_list; cur != NULL; cur = cur->next) {
 			if (new_state <= 0) {
@@ -368,8 +368,7 @@ int cond_normalize_expr(policydb_t * p, cond_node_t * cn)
 		if (ne) {
 			ne->next = NULL;
 		} else {	/* ne should never be NULL */
-			printf
-			    ("Found expr with no bools and only a ! - this should never happen.\n");
+			ERR(NULL, "Found expr with no bools and only a ! - this should never happen.\n");
 			return -1;
 		}
 		/* swap the true and false lists */
@@ -421,8 +420,7 @@ int cond_normalize_expr(policydb_t * p, cond_node_t * cn)
 			}
 			k = cond_evaluate_expr(p, cn->expr);
 			if (k == -1) {
-				printf
-				    ("While testing expression, expression result "
+				ERR(NULL, "While testing expression, expression result "
 				     "was undefined - this should never happen.\n");
 				return -1;
 			}
@@ -635,8 +633,7 @@ static int cond_insertf(avtab_t * a
 	 */
 	if (k->specified & AVTAB_TYPE) {
 		if (avtab_search(&p->te_avtab, k)) {
-			printf
-			    ("security: type rule already exists outside of a conditional.");
+			INFO(NULL, "security: type rule already exists outside of a conditional.");
 			goto err;
 		}
 		/*
@@ -652,8 +649,7 @@ static int cond_insertf(avtab_t * a
 			if (node_ptr) {
 				if (avtab_search_node_next
 				    (node_ptr, k->specified)) {
-					printf
-					    ("security: too many conflicting type rules.");
+					ERR(NULL, "security: too many conflicting type rules.");
 					goto err;
 				}
 				found = 0;
@@ -664,15 +660,13 @@ static int cond_insertf(avtab_t * a
 					}
 				}
 				if (!found) {
-					printf
-					    ("security: conflicting type rules.\n");
+					ERR(NULL, "security: conflicting type rules.\n");
 					goto err;
 				}
 			}
 		} else {
 			if (avtab_search(&p->te_cond_avtab, k)) {
-				printf
-				    ("security: conflicting type rules when adding type rule for true.\n");
+				ERR(NULL, "security: conflicting type rules when adding type rule for true.\n");
 				goto err;
 			}
 		}
@@ -680,7 +674,7 @@ static int cond_insertf(avtab_t * a
 
 	node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d);
 	if (!node_ptr) {
-		printf("security: could not insert rule.");
+		ERR(NULL, "security: could not insert rule.");
 		goto err;
 	}
 	node_ptr->parse_context = (void *)1;
@@ -742,14 +736,12 @@ static int cond_read_av_list(policydb_t * p, void *fp,
 static int expr_isvalid(policydb_t * p, cond_expr_t * expr)
 {
 	if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) {
-		printf
-		    ("security: conditional expressions uses unknown operator.\n");
+		INFO(NULL, "security: conditional expressions uses unknown operator.\n");
 		return 0;
 	}
 
 	if (expr->bool > p->p_bools.nprim) {
-		printf
-		    ("security: conditional expressions uses unknown bool.\n");
+		INFO(NULL, "security: conditional expressions uses unknown bool.\n");
 		return 0;
 	}
 	return 1;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 07/35] libsepol: use logging framework in ebitmap.c
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (5 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 06/35] libsepol: use logging framework in conditional.c Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 08/35] libsepol: use mallocarray wrapper to avoid overflows Christian Göttsche
                   ` (29 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Use the internal logging framework instead of directly writing to
stdout as it might be undesired to do so within a library.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/ebitmap.c | 27 ++++++++++-----------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/libsepol/src/ebitmap.c b/libsepol/src/ebitmap.c
index 1de3816a..fa728558 100644
--- a/libsepol/src/ebitmap.c
+++ b/libsepol/src/ebitmap.c
@@ -406,8 +406,7 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 	count = le32_to_cpu(buf[2]);
 
 	if (mapsize != MAPSIZE) {
-		printf
-		    ("security: ebitmap: map size %d does not match my size %zu (high bit was %d)\n",
+		ERR(NULL, "security: ebitmap: map size %d does not match my size %zu (high bit was %d)\n",
 		     mapsize, MAPSIZE, e->highbit);
 		goto bad;
 	}
@@ -416,8 +415,7 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 		goto ok;
 	}
 	if (e->highbit & (MAPSIZE - 1)) {
-		printf
-		    ("security: ebitmap: high bit (%d) is not a multiple of the map size (%zu)\n",
+		ERR(NULL, "security: ebitmap: high bit (%d) is not a multiple of the map size (%zu)\n",
 		     e->highbit, MAPSIZE);
 		goto bad;
 	}
@@ -429,12 +427,12 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 	for (i = 0; i < count; i++) {
 		rc = next_entry(buf, fp, sizeof(uint32_t));
 		if (rc < 0) {
-			printf("security: ebitmap: truncated map\n");
+			ERR(NULL, "security: ebitmap: truncated map\n");
 			goto bad;
 		}
 		n = (ebitmap_node_t *) malloc(sizeof(ebitmap_node_t));
 		if (!n) {
-			printf("security: ebitmap: out of memory\n");
+			ERR(NULL, "security: ebitmap: out of memory\n");
 			rc = -ENOMEM;
 			goto bad;
 		}
@@ -443,34 +441,30 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 		n->startbit = le32_to_cpu(buf[0]);
 
 		if (n->startbit & (MAPSIZE - 1)) {
-			printf
-			    ("security: ebitmap start bit (%d) is not a multiple of the map size (%zu)\n",
+			ERR(NULL, "security: ebitmap start bit (%d) is not a multiple of the map size (%zu)\n",
 			     n->startbit, MAPSIZE);
 			goto bad_free;
 		}
 		if (n->startbit > (e->highbit - MAPSIZE)) {
-			printf
-			    ("security: ebitmap start bit (%d) is beyond the end of the bitmap (%zu)\n",
+			ERR(NULL, "security: ebitmap start bit (%d) is beyond the end of the bitmap (%zu)\n",
 			     n->startbit, (e->highbit - MAPSIZE));
 			goto bad_free;
 		}
 		rc = next_entry(&map, fp, sizeof(uint64_t));
 		if (rc < 0) {
-			printf("security: ebitmap: truncated map\n");
+			ERR(NULL, "security: ebitmap: truncated map\n");
 			goto bad_free;
 		}
 		n->map = le64_to_cpu(map);
 
 		if (!n->map) {
-			printf
-			    ("security: ebitmap: null map in ebitmap (startbit %d)\n",
+			ERR(NULL, "security: ebitmap: null map in ebitmap (startbit %d)\n",
 			     n->startbit);
 			goto bad_free;
 		}
 		if (l) {
 			if (n->startbit <= l->startbit) {
-				printf
-				    ("security: ebitmap: start bit %d comes after start bit %d\n",
+				ERR(NULL, "security: ebitmap: start bit %d comes after start bit %d\n",
 				     n->startbit, l->startbit);
 				goto bad_free;
 			}
@@ -481,8 +475,7 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 		l = n;
 	}
 	if (count && l->startbit + MAPSIZE != e->highbit) {
-		printf
-		    ("security: ebitmap: high bit %u has not the expected value %zu\n",
+		ERR(NULL, "security: ebitmap: high bit %u has not the expected value %zu\n",
 		     e->highbit, l->startbit + MAPSIZE);
 		goto bad;
 	}
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 08/35] libsepol: use mallocarray wrapper to avoid overflows
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (6 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 07/35] libsepol: use logging framework in ebitmap.c Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 09/35] libsepol: use reallocarray " Christian Göttsche
                   ` (28 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Use a wrapper to guard `malloc(a * b)` type allocations, to detect
multiplication overflows, which result in too few memory being
allocated.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/conditional.c   | 2 +-
 libsepol/src/expand.c        | 4 ++--
 libsepol/src/hashtab.c       | 4 +++-
 libsepol/src/link.c          | 3 ++-
 libsepol/src/module.c        | 4 ++--
 libsepol/src/module_to_cil.c | 4 ++--
 libsepol/src/optimize.c      | 6 ++++--
 libsepol/src/policydb.c      | 6 +++---
 libsepol/src/private.h       | 9 +++++++++
 libsepol/src/services.c      | 6 +++---
 libsepol/src/sidtab.c        | 3 ++-
 libsepol/src/user_record.c   | 3 ++-
 libsepol/src/write.c         | 2 +-
 13 files changed, 36 insertions(+), 20 deletions(-)

diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c
index a01350a6..9a10aae1 100644
--- a/libsepol/src/conditional.c
+++ b/libsepol/src/conditional.c
@@ -522,7 +522,7 @@ int cond_init_bool_indexes(policydb_t * p)
 	if (p->bool_val_to_struct)
 		free(p->bool_val_to_struct);
 	p->bool_val_to_struct = (cond_bool_datum_t **)
-	    malloc(p->p_bools.nprim * sizeof(cond_bool_datum_t *));
+	    mallocarray(p->p_bools.nprim, sizeof(cond_bool_datum_t *));
 	if (!p->bool_val_to_struct)
 		return -1;
 	return 0;
diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
index a6a466f7..8a7259a0 100644
--- a/libsepol/src/expand.c
+++ b/libsepol/src/expand.c
@@ -3146,9 +3146,9 @@ int expand_module(sepol_handle_t * handle,
 		goto cleanup;
 
 	/* Build the type<->attribute maps and remove attributes. */
-	state.out->attr_type_map = malloc(state.out->p_types.nprim *
+	state.out->attr_type_map = mallocarray(state.out->p_types.nprim,
 					  sizeof(ebitmap_t));
-	state.out->type_attr_map = malloc(state.out->p_types.nprim *
+	state.out->type_attr_map = mallocarray(state.out->p_types.nprim,
 					  sizeof(ebitmap_t));
 	if (!state.out->attr_type_map || !state.out->type_attr_map) {
 		ERR(handle, "Out of memory!");
diff --git a/libsepol/src/hashtab.c b/libsepol/src/hashtab.c
index 21143b76..2eb35212 100644
--- a/libsepol/src/hashtab.c
+++ b/libsepol/src/hashtab.c
@@ -32,6 +32,8 @@
 #include <string.h>
 #include <sepol/policydb/hashtab.h>
 
+#include "private.h"
+
 hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h,
 						     const_hashtab_key_t key),
 			 int (*keycmp) (hashtab_t h,
@@ -52,7 +54,7 @@ hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h,
 	p->nel = 0;
 	p->hash_value = hash_value;
 	p->keycmp = keycmp;
-	p->htable = (hashtab_ptr_t *) malloc(sizeof(hashtab_ptr_t) * size);
+	p->htable = (hashtab_ptr_t *) mallocarray(size, sizeof(hashtab_ptr_t));
 	if (p->htable == NULL) {
 		free(p);
 		return NULL;
diff --git a/libsepol/src/link.c b/libsepol/src/link.c
index 461d2feb..a6b10b52 100644
--- a/libsepol/src/link.c
+++ b/libsepol/src/link.c
@@ -34,6 +34,7 @@
 #include <assert.h>
 
 #include "debug.h"
+#include "private.h"
 
 #undef min
 #define min(a,b) (((a) < (b)) ? (a) : (b))
@@ -1679,7 +1680,7 @@ static int copy_scope_index(scope_index_t * src, scope_index_t * dest,
 	}
 
 	/* next copy the enabled permissions data  */
-	if ((dest->class_perms_map = malloc(largest_mapped_class_value *
+	if ((dest->class_perms_map = mallocarray(largest_mapped_class_value,
 					    sizeof(*dest->class_perms_map))) ==
 	    NULL) {
 		goto cleanup;
diff --git a/libsepol/src/module.c b/libsepol/src/module.c
index 02a5de2c..4a51f25c 100644
--- a/libsepol/src/module.c
+++ b/libsepol/src/module.c
@@ -406,14 +406,14 @@ static int module_package_read_offsets(sepol_module_package_t * mod,
 		goto err;
 	}
 
-	off = (size_t *) malloc((nsec + 1) * sizeof(size_t));
+	off = (size_t *) mallocarray(nsec + 1, sizeof(size_t));
 	if (!off) {
 		ERR(file->handle, "out of memory");
 		goto err;
 	}
 
 	free(buf);
-	buf = malloc(sizeof(uint32_t) * nsec);
+	buf = mallocarray(nsec, sizeof(uint32_t));
 	if (!buf) {
 		ERR(file->handle, "out of memory");
 		goto err;
diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c
index 16e4004e..ad0880bd 100644
--- a/libsepol/src/module_to_cil.c
+++ b/libsepol/src/module_to_cil.c
@@ -430,7 +430,7 @@ static int stack_init(struct stack **stack)
 		goto exit;
 	}
 
-	s->stack = malloc(sizeof(*s->stack) * STACK_SIZE);
+	s->stack = mallocarray(STACK_SIZE, sizeof(*s->stack));
 	if (s->stack == NULL) {
 		goto exit;
 	}
@@ -1008,7 +1008,7 @@ static int ebitmap_to_names(struct ebitmap *map, char **vals_to_names, char ***n
 		goto exit;
 	}
 
-	name_arr = malloc(sizeof(*name_arr) * num);
+	name_arr = mallocarray(num, sizeof(*name_arr));
 	if (name_arr == NULL) {
 		log_err("Out of memory");
 		rc = -1;
diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c
index 6826155c..f8298fb7 100644
--- a/libsepol/src/optimize.c
+++ b/libsepol/src/optimize.c
@@ -31,6 +31,8 @@
 #include <sepol/policydb/policydb.h>
 #include <sepol/policydb/conditional.h>
 
+#include "private.h"
+
 #define TYPE_VEC_INIT_SIZE 16
 
 struct type_vec {
@@ -42,7 +44,7 @@ static int type_vec_init(struct type_vec *v)
 {
 	v->capacity = TYPE_VEC_INIT_SIZE;
 	v->count = 0;
-	v->types = malloc(v->capacity * sizeof(*v->types));
+	v->types = mallocarray(v->capacity, sizeof(*v->types));
 	if (!v->types)
 		return -1;
 	return 0;
@@ -93,7 +95,7 @@ static struct type_vec *build_type_map(const policydb_t *p)
 {
 	unsigned int i, k;
 	ebitmap_node_t *n;
-	struct type_vec *map = malloc(p->p_types.nprim * sizeof(*map));
+	struct type_vec *map = mallocarray(p->p_types.nprim, sizeof(*map));
 	if (!map)
 		return NULL;
 
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 7093d9b7..46fb4893 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -4111,7 +4111,7 @@ static int scope_read(policydb_t * p, int symnum, struct policy_file *fp)
 		goto cleanup;
 	}
 	if ((scope->decl_ids =
-	     malloc(scope->decl_ids_len * sizeof(uint32_t))) == NULL) {
+	     mallocarray(scope->decl_ids_len, sizeof(uint32_t))) == NULL) {
 		goto cleanup;
 	}
 	rc = next_entry(scope->decl_ids, fp, sizeof(uint32_t) * scope->decl_ids_len);
@@ -4500,8 +4500,8 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose)
 	}
 
 	if (policy_type == POLICY_KERN) {
-		p->type_attr_map = malloc(p->p_types.nprim * sizeof(ebitmap_t));
-		p->attr_type_map = malloc(p->p_types.nprim * sizeof(ebitmap_t));
+		p->type_attr_map = mallocarray(p->p_types.nprim, sizeof(ebitmap_t));
+		p->attr_type_map = mallocarray(p->p_types.nprim, sizeof(ebitmap_t));
 		if (!p->type_attr_map || !p->attr_type_map)
 			goto bad;
 		for (i = 0; i < p->p_types.nprim; i++) {
diff --git a/libsepol/src/private.h b/libsepol/src/private.h
index 6146f59f..d3d65a57 100644
--- a/libsepol/src/private.h
+++ b/libsepol/src/private.h
@@ -83,3 +83,12 @@ extern int next_entry(void *buf, struct policy_file *fp, size_t bytes);
 extern size_t put_entry(const void *ptr, size_t size, size_t n,
 		        struct policy_file *fp);
 extern int str_read(char **strp, struct policy_file *fp, size_t len);
+
+static inline void* mallocarray(size_t nmemb, size_t size) {
+	if (size && nmemb > (size_t)-1 / size) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	return malloc(nmemb * size);
+}
diff --git a/libsepol/src/services.c b/libsepol/src/services.c
index 673b3971..a132d080 100644
--- a/libsepol/src/services.c
+++ b/libsepol/src/services.c
@@ -712,7 +712,7 @@ mls_ops:
 	 * Generate the same number of answer buffer entries as expression
 	 * buffers (as there will never be more).
 	 */
-	answer_list = malloc(expr_count * sizeof(*answer_list));
+	answer_list = mallocarray(expr_count, sizeof(*answer_list));
 	if (!answer_list) {
 		ERR(NULL, "failed to allocate answer stack");
 		rc = -ENOMEM;
@@ -2163,7 +2163,7 @@ int sepol_get_user_sids(sepol_security_id_t fromsid,
 	}
 	usercon.user = user->s.value;
 
-	mysids = malloc(maxnel * sizeof(sepol_security_id_t));
+	mysids = mallocarray(maxnel, sizeof(sepol_security_id_t));
 	if (!mysids) {
 		rc = -ENOMEM;
 		goto out;
@@ -2199,7 +2199,7 @@ int sepol_get_user_sids(sepol_security_id_t fromsid,
 			} else {
 				maxnel += SIDS_NEL;
 				mysids2 =
-				    malloc(maxnel *
+				    mallocarray(maxnel,
 					   sizeof(sepol_security_id_t));
 
 				if (!mysids2) {
diff --git a/libsepol/src/sidtab.c b/libsepol/src/sidtab.c
index 255e0725..adeae6eb 100644
--- a/libsepol/src/sidtab.c
+++ b/libsepol/src/sidtab.c
@@ -15,6 +15,7 @@
 #include <sepol/policydb/sidtab.h>
 
 #include "flask.h"
+#include "private.h"
 
 #define SIDTAB_HASH(sid) \
 (sid & SIDTAB_HASH_MASK)
@@ -27,7 +28,7 @@ int sepol_sidtab_init(sidtab_t * s)
 {
 	int i;
 
-	s->htable = malloc(sizeof(sidtab_ptr_t) * SIDTAB_SIZE);
+	s->htable = mallocarray(SIDTAB_SIZE, sizeof(sidtab_ptr_t));
 	if (!s->htable)
 		return -ENOMEM;
 	for (i = 0; i < SIDTAB_SIZE; i++)
diff --git a/libsepol/src/user_record.c b/libsepol/src/user_record.c
index ac520060..c1356a6b 100644
--- a/libsepol/src/user_record.c
+++ b/libsepol/src/user_record.c
@@ -4,6 +4,7 @@
 
 #include "user_internal.h"
 #include "debug.h"
+#include "private.h"
 
 struct sepol_user {
 	/* This user's name */
@@ -265,7 +266,7 @@ int sepol_user_get_roles(sepol_handle_t * handle,
 
 	unsigned int i;
 	const char **tmp_roles =
-	    (const char **)malloc(sizeof(char *) * user->num_roles);
+	    (const char **)mallocarray(user->num_roles, sizeof(char *));
 	if (!tmp_roles)
 		goto omem;
 
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index 3bd034d6..9df5b0bd 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -2117,7 +2117,7 @@ static int scope_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
 		 * buffer.  this would have been easier with C99's
 		 * dynamic arrays... */
 		rc = POLICYDB_ERROR;
-		dyn_buf = malloc(items * sizeof(*dyn_buf));
+		dyn_buf = mallocarray(items, sizeof(*dyn_buf));
 		if (!dyn_buf)
 			goto err;
 		buf = dyn_buf;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 09/35] libsepol: use reallocarray wrapper to avoid overflows
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (7 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 08/35] libsepol: use mallocarray wrapper to avoid overflows Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 10/35] libsepol: add checks for read sizes Christian Göttsche
                   ` (27 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Use a wrapper to guard `realloc(p, a * b)` type allocations, to detect
multiplication overflows, which result in too few memory being
allocated.

Use a custom implementation if the used C library does not offer one.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/Makefile           |  6 ++++++
 libsepol/src/kernel_to_common.c |  4 ++--
 libsepol/src/module_to_cil.c    |  9 +++++----
 libsepol/src/optimize.c         |  5 +++--
 libsepol/src/private.h          | 11 +++++++++++
 libsepol/src/services.c         |  6 +++---
 libsepol/src/user_record.c      |  5 +++--
 libsepol/src/users.c            | 12 ++++++------
 libsepol/src/util.c             | 11 +++++++----
 9 files changed, 46 insertions(+), 23 deletions(-)

diff --git a/libsepol/src/Makefile b/libsepol/src/Makefile
index dc8b1773..13410c67 100644
--- a/libsepol/src/Makefile
+++ b/libsepol/src/Makefile
@@ -29,6 +29,12 @@ LOBJS += $(sort $(patsubst %.c,%.lo,$(sort $(wildcard $(CILDIR)/src/*.c)) $(CIL_
 override CFLAGS += -I$(CILDIR)/include
 endif
 
+# check for reallocarray(3) availability
+H := \#
+ifeq (yes,$(shell printf '${H}define _GNU_SOURCE\n${H}include <stdlib.h>\nint main(void){void*p=reallocarray(NULL, 1, sizeof(char));return 0;}' | $(CC) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
+override CFLAGS += -DHAVE_REALLOCARRAY
+endif
+
 LD_SONAME_FLAGS=-soname,$(LIBSO),--version-script=$(LIBMAP),-z,defs
 
 LN=ln
diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c
index a7453d3c..51df8c25 100644
--- a/libsepol/src/kernel_to_common.c
+++ b/libsepol/src/kernel_to_common.c
@@ -161,7 +161,7 @@ int strs_add(struct strs *strs, char *s)
 		char **new;
 		unsigned i = strs->size;
 		strs->size *= 2;
-		new = realloc(strs->list, sizeof(char *)*strs->size);
+		new = reallocarray(strs->list, strs->size, sizeof(char *));
 		if (!new) {
 			sepol_log_err("Out of memory");
 			return -1;
@@ -220,7 +220,7 @@ int strs_add_at_index(struct strs *strs, char *s, unsigned index)
 		while (index >= strs->size) {
 			strs->size *= 2;
 		}
-		new = realloc(strs->list, sizeof(char *)*strs->size);
+		new = reallocarray(strs->list, strs->size, sizeof(char *));
 		if (!new) {
 			sepol_log_err("Out of memory");
 			return -1;
diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c
index ad0880bd..84e49c5b 100644
--- a/libsepol/src/module_to_cil.c
+++ b/libsepol/src/module_to_cil.c
@@ -453,7 +453,7 @@ static int stack_push(struct stack *stack, void *ptr)
 	void *new_stack;
 
 	if (stack->pos + 1 == stack->size) {
-		new_stack = realloc(stack->stack, sizeof(*stack->stack) * (stack->size * 2));
+		new_stack = reallocarray(stack->stack, stack->size * 2, sizeof(*stack->stack));
 		if (new_stack == NULL) {
 			goto exit;
 		}
@@ -4123,7 +4123,7 @@ exit:
 static int fp_to_buffer(FILE *fp, char **data, size_t *data_len)
 {
 	int rc = -1;
-	char *d = NULL;
+	char *d = NULL, *d_tmp;
 	size_t d_len = 0;
 	size_t read_len = 0;
 	size_t max_len = 1 << 17; // start at 128KB, this is enough to hold about half of all the existing pp files
@@ -4139,12 +4139,13 @@ static int fp_to_buffer(FILE *fp, char **data, size_t *data_len)
 		d_len += read_len;
 		if (d_len == max_len) {
 			max_len *= 2;
-			d = realloc(d, max_len);
-			if (d == NULL) {
+			d_tmp = realloc(d, max_len);
+			if (d_tmp == NULL) {
 				log_err("Out of memory");
 				rc = -1;
 				goto exit;
 			}
+			d = d_tmp;
 		}
 	}
 
diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c
index f8298fb7..8a048702 100644
--- a/libsepol/src/optimize.c
+++ b/libsepol/src/optimize.c
@@ -59,8 +59,9 @@ static int type_vec_append(struct type_vec *v, uint32_t type)
 {
 	if (v->capacity == v->count) {
 		unsigned int new_capacity = v->capacity * 2;
-		uint32_t *new_types = realloc(v->types,
-					      new_capacity * sizeof(*v->types));
+		uint32_t *new_types = reallocarray(v->types,
+						   new_capacity,
+						   sizeof(*v->types));
 		if (!new_types)
 			return -1;
 
diff --git a/libsepol/src/private.h b/libsepol/src/private.h
index d3d65a57..a8cc1472 100644
--- a/libsepol/src/private.h
+++ b/libsepol/src/private.h
@@ -92,3 +92,14 @@ static inline void* mallocarray(size_t nmemb, size_t size) {
 
 	return malloc(nmemb * size);
 }
+
+#ifndef HAVE_REALLOCARRAY
+static inline void* reallocarray(void *ptr, size_t nmemb, size_t size) {
+	if (size && nmemb > (size_t)-1 / size) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	return realloc(ptr, nmemb * size);
+}
+#endif
diff --git a/libsepol/src/services.c b/libsepol/src/services.c
index a132d080..e9333741 100644
--- a/libsepol/src/services.c
+++ b/libsepol/src/services.c
@@ -94,7 +94,7 @@ static void push(char *expr_ptr)
 		else
 			new_stack_len = stack_len * 2;
 
-		new_stack = realloc(stack, new_stack_len * sizeof(*stack));
+		new_stack = reallocarray(stack, new_stack_len, sizeof(*stack));
 		if (!new_stack) {
 			ERR(NULL, "unable to allocate stack space");
 			return;
@@ -449,8 +449,8 @@ static int constraint_expr_eval_reason(context_struct_t *scontext,
 			else
 				new_expr_list_len = expr_list_len * 2;
 
-			new_expr_list = realloc(expr_list,
-					new_expr_list_len * sizeof(*expr_list));
+			new_expr_list = reallocarray(expr_list,
+					new_expr_list_len, sizeof(*expr_list));
 			if (!new_expr_list) {
 				ERR(NULL, "failed to allocate expr buffer stack");
 				rc = -ENOMEM;
diff --git a/libsepol/src/user_record.c b/libsepol/src/user_record.c
index c1356a6b..404fa3a8 100644
--- a/libsepol/src/user_record.c
+++ b/libsepol/src/user_record.c
@@ -183,8 +183,9 @@ int sepol_user_add_role(sepol_handle_t * handle,
 	if (!role_cp)
 		goto omem;
 
-	roles_realloc = realloc(user->roles,
-				sizeof(char *) * (user->num_roles + 1));
+	roles_realloc = reallocarray(user->roles,
+				     user->num_roles + 1,
+				     sizeof(char *));
 	if (!roles_realloc)
 		goto omem;
 
diff --git a/libsepol/src/users.c b/libsepol/src/users.c
index b895b7f5..a7406214 100644
--- a/libsepol/src/users.c
+++ b/libsepol/src/users.c
@@ -226,17 +226,17 @@ int sepol_user_modify(sepol_handle_t * handle,
 		void *tmp_ptr;
 
 		/* Ensure reverse lookup array has enough space */
-		tmp_ptr = realloc(policydb->user_val_to_struct,
-				  (policydb->p_users.nprim +
-				   1) * sizeof(user_datum_t *));
+		tmp_ptr = reallocarray(policydb->user_val_to_struct,
+				  policydb->p_users.nprim + 1,
+				  sizeof(user_datum_t *));
 		if (!tmp_ptr)
 			goto omem;
 		policydb->user_val_to_struct = tmp_ptr;
 		policydb->user_val_to_struct[policydb->p_users.nprim] = NULL;
 
-		tmp_ptr = realloc(policydb->sym_val_to_name[SYM_USERS],
-				  (policydb->p_users.nprim +
-				   1) * sizeof(char *));
+		tmp_ptr = reallocarray(policydb->sym_val_to_name[SYM_USERS],
+				  policydb->p_users.nprim + 1,
+				  sizeof(char *));
 		if (!tmp_ptr)
 			goto omem;
 		policydb->sym_val_to_name[SYM_USERS] = tmp_ptr;
diff --git a/libsepol/src/util.c b/libsepol/src/util.c
index 902c63c5..b7230564 100644
--- a/libsepol/src/util.c
+++ b/libsepol/src/util.c
@@ -40,6 +40,8 @@ struct val_to_name {
  * 0).  Return 0 on success, -1 on out of memory. */
 int add_i_to_a(uint32_t i, uint32_t * cnt, uint32_t ** a)
 {
+	uint32_t *new;
+
 	if (cnt == NULL || a == NULL)
 		return -1;
 
@@ -48,17 +50,18 @@ int add_i_to_a(uint32_t i, uint32_t * cnt, uint32_t ** a)
 	 * than be smart about it, for now we realloc() the array each
 	 * time a new uint32_t is added! */
 	if (*a != NULL)
-		*a = (uint32_t *) realloc(*a, (*cnt + 1) * sizeof(uint32_t));
+		new = (uint32_t *) reallocarray(*a, *cnt + 1, sizeof(uint32_t));
 	else {			/* empty list */
 
 		*cnt = 0;
-		*a = (uint32_t *) malloc(sizeof(uint32_t));
+		new = (uint32_t *) malloc(sizeof(uint32_t));
 	}
-	if (*a == NULL) {
+	if (new == NULL) {
 		return -1;
 	}
-	(*a)[*cnt] = i;
+	new[*cnt] = i;
 	(*cnt)++;
+	*a = new;
 	return 0;
 }
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 10/35] libsepol: add checks for read sizes
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (8 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 09/35] libsepol: use reallocarray " Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 11/35] libsepol: enforce avtab item limit Christian Göttsche
                   ` (26 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Add checks for invalid read sizes from a binary policy to guard
allocations.

In the fuzzer build the value will also be bounded to avoid oom reports.

    ==29857== ERROR: libFuzzer: out-of-memory (malloc(17179868160))
       To change the out-of-memory limit use -rss_limit_mb=<N>

        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aa143 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa143)
        #7 0x5259cb in malloc (./out/binpolicy-fuzzer+0x5259cb)
        #8 0x580b5d in mallocarray ./libsepol/src/./private.h:93:9
        #9 0x57c2ed in scope_read ./libsepol/src/policydb.c:4120:7
        #10 0x576b0d in policydb_read ./libsepol/src/policydb.c:4462:9
        #11 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
        #12 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #13 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #14 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #15 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #16 0x7ffad6e107ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #17 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

    ==19462== ERROR: libFuzzer: out-of-memory (malloc(18253611008))
       To change the out-of-memory limit use -rss_limit_mb=<N>

        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aa999 in __asan::asan_calloc(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa999)
        #7 0x525b63 in __interceptor_calloc (./out/binpolicy-fuzzer+0x525b63)
        #8 0x570938 in policydb_index_others ./libsepol/src/policydb.c:1245:6
        #9 0x5771f3 in policydb_read ./src/policydb.c:4481:6
        #10 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
        #11 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #12 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #13 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #14 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #15 0x7f4d933157ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #16 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 46fb4893..70b503e1 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -2103,6 +2103,8 @@ static int common_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
 	if (symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE))
 		goto bad;
 	comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+	if (is_saturated(comdatum->permissions.nprim))
+		goto bad;
 	nel = le32_to_cpu(buf[3]);
 
 	key = malloc(len + 1);
@@ -2251,6 +2253,8 @@ static int class_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
 	if (symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE))
 		goto bad;
 	cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+	if (is_saturated(cladatum->permissions.nprim))
+		goto bad;
 	nel = le32_to_cpu(buf[4]);
 
 	ncons = le32_to_cpu(buf[5]);
@@ -3980,6 +3984,8 @@ static int avrule_decl_read(policydb_t * p, avrule_decl_t * decl,
 		if (rc < 0) 
 			return -1;
 		nprim = le32_to_cpu(buf[0]);
+		if (is_saturated(nprim))
+			return -1;
 		nel = le32_to_cpu(buf[1]);
 		for (j = 0; j < nel; j++) {
 			if (read_f[i] (p, decl->symtab[i].table, fp)) {
@@ -4106,7 +4112,7 @@ static int scope_read(policydb_t * p, int symnum, struct policy_file *fp)
 		goto cleanup;
 	scope->scope = le32_to_cpu(buf[0]);
 	scope->decl_ids_len = le32_to_cpu(buf[1]);
-	if (scope->decl_ids_len == 0) {
+	if (zero_or_saturated(scope->decl_ids_len)) {
 		ERR(fp->handle, "invalid scope with no declaration");
 		goto cleanup;
 	}
@@ -4396,6 +4402,8 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose)
 		if (rc < 0)
 			goto bad;
 		nprim = le32_to_cpu(buf[0]);
+		if (is_saturated(nprim))
+			goto bad;
 		nel = le32_to_cpu(buf[1]);
 		if (nel && !nprim) {
 			ERR(fp->handle, "unexpected items in symbol table with no symbol");
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 11/35] libsepol: enforce avtab item limit
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (9 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 10/35] libsepol: add checks for read sizes Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 12/35] libsepol: clean memory on conditional read failure Christian Göttsche
                   ` (25 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Check the current item count does not exceed the maximum allowed to
avoid stack overflows.

    ==33660==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fa64b8fc070 at pc 0x0000005acba0 bp 0x7ffc1f0b2870 sp 0x7ffc1f0b2868
    READ of size 4 at 0x7fa64b8fc070 thread T0
        #0 0x5acb9f in avtab_read_item ./libsepol/src/avtab.c:507:18
        #1 0x5acec4 in avtab_read ./libsepol/src/avtab.c:611:8
        #2 0x576ae3 in policydb_read ./libsepol/src/policydb.c:4433:7
        #3 0x55a1fe in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:24:6
        #4 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #5 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #6 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #7 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #8 0x7fa64cc867ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #9 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

    Address 0x7fa64b8fc070 is located in stack of thread T0 at offset 112 in frame
        #0 0x5aabdf in avtab_read_item ./libsepol/src/avtab.c:437

      This frame has 6 object(s):
        [32, 33) 'buf8' (line 438)
        [48, 56) 'buf16' (line 439)
        [80, 112) 'buf32' (line 440) <== Memory access at offset 112 overflows this variable
        [144, 152) 'key' (line 441)
        [176, 192) 'datum' (line 442)
        [208, 244) 'xperms' (line 443)
    HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
          (longjmp and C++ exceptions *are* supported)
    SUMMARY: AddressSanitizer: stack-buffer-overflow ./libsepol/src/avtab.c:507:18 in avtab_read_item
    Shadow bytes around the buggy address:
      0x0ff5497177b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff5497177c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff5497177d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff5497177e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff5497177f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    =>0x0ff549717800: f1 f1 f1 f1 01 f2 00 f2 f2 f2 00 00 00 00[f2]f2
      0x0ff549717810: f2 f2 00 f2 f2 f2 00 00 f2 f2 00 00 00 00 04 f3
      0x0ff549717820: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff549717830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff549717840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff549717850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    Shadow byte legend (one shadow byte represents 8 application bytes):
      Addressable:           00
      Partially addressable: 01 02 03 04 05 06 07
      Heap left redzone:       fa
      Freed heap region:       fd
      Stack left redzone:      f1
      Stack mid redzone:       f2
      Stack right redzone:     f3
      Stack after return:      f5
      Stack use after scope:   f8
      Global redzone:          f9
      Global init order:       f6
      Poisoned by user:        f7
      Container overflow:      fc
      Array cookie:            ac
      Intra object redzone:    bb
      ASan internal:           fe
      Left alloca redzone:     ca
      Right alloca redzone:    cb
    ==33660==ABORTING

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/avtab.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libsepol/src/avtab.c b/libsepol/src/avtab.c
index 93505b20..2a52c69a 100644
--- a/libsepol/src/avtab.c
+++ b/libsepol/src/avtab.c
@@ -503,6 +503,12 @@ int avtab_read_item(struct policy_file *fp, uint32_t vers, avtab_t * a,
 
 		for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
 			if (val & spec_order[i]) {
+				if (items > items2) {
+					ERR(fp->handle,
+					    "entry has too many items (%d/%d)",
+					    items, items2);
+					return -1;
+				}
 				key.specified = spec_order[i] | enabled;
 				datum.data = le32_to_cpu(buf32[items++]);
 				rc = insertf(a, &key, &datum, p);
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 12/35] libsepol: clean memory on conditional read failure
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (10 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 11/35] libsepol: enforce avtab item limit Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-13 14:10   ` James Carter
  2021-10-11 16:25 ` [RFC PATCH 13/35] libsepol: validate MLS levels Christian Göttsche
                   ` (24 subsequent siblings)
  36 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Free the local access vector list on failure as it does not get moved
into the policy structure.

    Direct leak of 16 byte(s) in 1 object(s) allocated from:
        #0 0x52596d in malloc (./out/binpolicy-fuzzer+0x52596d)
        #1 0x5b30d2 in cond_insertf ./libsepol/src/conditional.c:682:9
        #2 0x5ac218 in avtab_read_item ./libsepol/src/avtab.c:583:10
        #3 0x5b21f4 in cond_read_av_list ./libsepol/src/conditional.c:725:8
        #4 0x5b21f4 in cond_read_node ./libsepol/src/conditional.c:798:7
        #5 0x5b21f4 in cond_read_list ./libsepol/src/conditional.c:847:7
        #6 0x576b6e in policydb_read ./libsepol/src/policydb.c:4436:8
        #7 0x55a1fe in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:24:6
        #8 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #9 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #10 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #11 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #12 0x7f47abeb87ec in __libc_start_main csu/../csu/libc-start.c:332:16

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/conditional.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c
index 9a10aae1..50cb5395 100644
--- a/libsepol/src/conditional.c
+++ b/libsepol/src/conditional.c
@@ -724,8 +724,10 @@ static int cond_read_av_list(policydb_t * p, void *fp,
 	for (i = 0; i < len; i++) {
 		rc = avtab_read_item(fp, p->policyvers, &p->te_cond_avtab,
 				     cond_insertf, &data);
-		if (rc)
+		if (rc) {
+			cond_av_list_destroy(data.head);
 			return rc;
+		}
 
 	}
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 13/35] libsepol: validate MLS levels
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (11 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 12/35] libsepol: clean memory on conditional read failure Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-13 15:38   ` James Carter
  2021-10-11 16:25 ` [RFC PATCH 14/35] libsepol: reject invalid fsuse types Christian Göttsche
                   ` (23 subsequent siblings)
  36 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Validate the level map of the policy to ensure no level refers to a non
existent category.

READ of size 8 at 0x602000000c58 thread T0
    #0 0x568d2c in cats_ebitmap_len ./libsepol/src/kernel_to_conf.c:1003:14
    #1 0x568d2c in cats_ebitmap_to_str ./libsepol/src/kernel_to_conf.c:1038:19
    #2 0x55e371 in write_level_rules_to_conf ./libsepol/src/kernel_to_conf.c:1106:11
    #3 0x55e371 in write_mls_rules_to_conf ./libsepol/src/kernel_to_conf.c:1140:7
    #4 0x55adb1 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3103:7
    #5 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
    #6 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
    #7 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
    #8 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
    #9 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
    #10 0x7f741d0d67ec in __libc_start_main csu/../csu/libc-start.c:332:16
    #11 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 5804d247..ca0dcca3 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -310,6 +310,29 @@ bad:
 	return -1;
 }
 
+static int validate_mls_level(mls_level_t *level, validate_t *sens, validate_t *cats)
+{
+	if (level->sens == 0)
+		return 0;
+	if (validate_value(level->sens, sens))
+		goto bad;
+	if (validate_ebitmap(&level->cat, cats))
+		goto bad;
+
+	return 0;
+
+	bad:
+	return -1;
+}
+
+static int validate_level(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	level_datum_t *level = d;
+	validate_t *flavors = args;
+
+	return validate_mls_level(level->level, &flavors[SYM_LEVELS], &flavors[SYM_CATS]);
+}
+
 static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
 {
 	unsigned int i;
@@ -368,6 +391,9 @@ static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate
 		}
 	}
 
+	if (hashtab_map(p->p_levels.table, validate_level, flavors))
+		goto bad;
+
 	return 0;
 
 bad:
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 14/35] libsepol: reject invalid fsuse types
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (12 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 13/35] libsepol: validate MLS levels Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-18 19:57   ` James Carter
  2021-10-11 16:25 ` [RFC PATCH 15/35] libsepol: reject invalid default targets Christian Göttsche
                   ` (22 subsequent siblings)
  36 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Reject loading a policy with invalid fsuse declarations, except xattr,
trans and task, so that all following code, e.g. the different output
modes, do not need to handle unsupported ones.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 70b503e1..980af059 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -48,6 +48,7 @@
 #include <sepol/policydb/expand.h>
 #include <sepol/policydb/conditional.h>
 #include <sepol/policydb/avrule_block.h>
+#include <sepol/policydb/services.h>
 #include <sepol/policydb/util.h>
 
 #include "kernel_to_common.h"
@@ -3099,6 +3100,14 @@ static int ocontext_read_selinux(const struct policydb_compat_info *info,
 				if (rc < 0)
 					return -1;
 				c->v.behavior = le32_to_cpu(buf[0]);
+				switch (c->v.behavior) {
+				case SECURITY_FS_USE_XATTR:
+				case SECURITY_FS_USE_TRANS:
+				case SECURITY_FS_USE_TASK:
+					break;
+				default:
+					return -1;
+				}
 				len = le32_to_cpu(buf[1]);
 				if (zero_or_saturated(len))
 					return -1;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 15/35] libsepol: reject invalid default targets
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (13 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 14/35] libsepol: reject invalid fsuse types Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-18 19:58   ` James Carter
  2021-10-11 16:25 ` [RFC PATCH 16/35] libsepol: validate expanded user range and level Christian Göttsche
                   ` (21 subsequent siblings)
  36 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Reject loading a policy with invalid default targets so that all
following code, e.g. the different output modes, do not need to handle
unsupported ones.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 980af059..5e8b4a3f 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -2313,8 +2313,37 @@ static int class_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
 		if (rc < 0)
 			goto bad;
 		cladatum->default_user = le32_to_cpu(buf[0]);
+		switch (cladatum->default_user) {
+		case 0:
+		case DEFAULT_SOURCE:
+		case DEFAULT_TARGET:
+			break;
+		default:
+			goto bad;
+		}
 		cladatum->default_role = le32_to_cpu(buf[1]);
+		switch (cladatum->default_role) {
+		case 0:
+		case DEFAULT_SOURCE:
+		case DEFAULT_TARGET:
+			break;
+		default:
+			goto bad;
+		}
 		cladatum->default_range = le32_to_cpu(buf[2]);
+		switch (cladatum->default_range) {
+		case 0:
+		case DEFAULT_SOURCE_LOW:
+		case DEFAULT_SOURCE_HIGH:
+		case DEFAULT_SOURCE_LOW_HIGH:
+		case DEFAULT_TARGET_LOW:
+		case DEFAULT_TARGET_HIGH:
+		case DEFAULT_TARGET_LOW_HIGH:
+		case DEFAULT_GLBLUB:
+			break;
+		default:
+			goto bad;
+		}
 	}
 
 	if ((p->policy_type == POLICY_KERN &&
@@ -2325,6 +2354,14 @@ static int class_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
 		if (rc < 0)
 			goto bad;
 		cladatum->default_type = le32_to_cpu(buf[0]);
+		switch (cladatum->default_type) {
+		case 0:
+		case DEFAULT_SOURCE:
+		case DEFAULT_TARGET:
+			break;
+		default:
+			goto bad;
+		}
 	}
 
 	if (hashtab_insert(h, key, cladatum))
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 16/35] libsepol: validate expanded user range and level
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (14 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 15/35] libsepol: reject invalid default targets Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 17/35] libsepol: validate types Christian Göttsche
                   ` (20 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Check those contains valid values.

    ==57532==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000001178 at pc 0x000000564c04 bp 0x7ffed7a5ad90 sp 0x7ffed7a5ad88
    READ of size 8 at 0x603000001178 thread T0
        #0 0x564c03 in level_to_str ./libsepol/src/kernel_to_conf.c:1901:19
        #1 0x564c03 in range_to_str ./libsepol/src/kernel_to_conf.c:1926:9
        #2 0x564c03 in write_user_decl_rules_to_conf ./libsepol/src/kernel_to_conf.c:2367:12
        #3 0x55b137 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3184:7
        #4 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #5 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #6 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #7 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #8 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #9 0x7f2c2e1a77ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #10 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 47 +++++++++++++++++++++-----------
 1 file changed, 31 insertions(+), 16 deletions(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index ca0dcca3..a6ae728a 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -290,38 +290,53 @@ bad:
 	return -1;
 }
 
-static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, validate_t flavors[])
+static int validate_mls_level(mls_level_t *level, validate_t *sens, validate_t *cats)
 {
-	if (validate_value(user->s.value, &flavors[SYM_USERS]))
-		goto bad;
-	if (validate_role_set(&user->roles, &flavors[SYM_ROLES]))
+	if (validate_value(level->sens, sens))
 		goto bad;
-	if (validate_mls_semantic_range(&user->range, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+	if (validate_ebitmap(&level->cat, cats))
 		goto bad;
-	if (validate_mls_semantic_level(&user->dfltlevel, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+
+	return 0;
+
+	bad:
+	return -1;
+}
+
+static int validate_mls_range(mls_range_t *range, validate_t *sens, validate_t *cats)
+{
+	if (validate_mls_level(&range->level[0], sens, cats))
 		goto bad;
-	if (user->bounds && validate_value(user->bounds, &flavors[SYM_USERS]))
+	if (validate_mls_level(&range->level[1], sens, cats))
 		goto bad;
 
 	return 0;
 
-bad:
-	ERR(handle, "Invalid user datum");
+	bad:
 	return -1;
 }
 
-static int validate_mls_level(mls_level_t *level, validate_t *sens, validate_t *cats)
+static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, validate_t flavors[], int mls)
 {
-	if (level->sens == 0)
-		return 0;
-	if (validate_value(level->sens, sens))
+	if (validate_value(user->s.value, &flavors[SYM_USERS]))
 		goto bad;
-	if (validate_ebitmap(&level->cat, cats))
+	if (validate_role_set(&user->roles, &flavors[SYM_ROLES]))
+		goto bad;
+	if (validate_mls_semantic_range(&user->range, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		goto bad;
+	if (validate_mls_semantic_level(&user->dfltlevel, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		goto bad;
+	if (mls && validate_mls_range(&user->exp_range, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		goto bad;
+	if (mls && validate_mls_level(&user->exp_dfltlevel, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		goto bad;
+	if (user->bounds && validate_value(user->bounds, &flavors[SYM_USERS]))
 		goto bad;
 
 	return 0;
 
-	bad:
+bad:
+	ERR(handle, "Invalid user datum");
 	return -1;
 }
 
@@ -383,7 +398,7 @@ static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate
 		if (p->user_val_to_struct[i]) {
 			if (ebitmap_get_bit(&flavors[SYM_USERS].gaps, i))
 				goto bad;
-			if (validate_user_datum(handle, p->user_val_to_struct[i], flavors))
+			if (validate_user_datum(handle, p->user_val_to_struct[i], flavors, p->mls))
 				goto bad;
 		} else {
 			if (!ebitmap_get_bit(&flavors[SYM_USERS].gaps, i))
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 17/35] libsepol: validate types
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (15 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 16/35] libsepol: validate expanded user range and level Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-13 15:39   ` James Carter
  2021-10-11 16:25 ` [RFC PATCH 18/35] libsepol: use size_t for indexes in strs helpers Christian Göttsche
                   ` (19 subsequent siblings)
  36 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Check all types are valid values, especially important for aliases.

    ==9702==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000af8 at pc 0x000000560698 bp 0x7ffcca93b9f0 sp 0x7ffcca93b9e8
    READ of size 8 at 0x602000000af8 thread T0
        #0 0x560697 in write_type_alias_rules_to_conf ./libsepol/src/kernel_to_conf.c:1424:10
        #1 0x55af16 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3131:7
        #2 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #3 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #4 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #5 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #6 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #7 0x7f518b1d57ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #8 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index a6ae728a..c9700399 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -348,6 +348,14 @@ static int validate_level(__attribute__ ((unused))hashtab_key_t k, hashtab_datum
 	return validate_mls_level(level->level, &flavors[SYM_LEVELS], &flavors[SYM_CATS]);
 }
 
+static int validate_datum(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	symtab_datum_t *s = d;
+	uint32_t *nprim = (uint32_t *)args;
+
+	return !value_isvalid(s->value, *nprim);
+}
+
 static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
 {
 	unsigned int i;
@@ -406,6 +414,9 @@ static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate
 		}
 	}
 
+	if (hashtab_map(p->p_types.table, validate_datum, &flavors[SYM_TYPES]))
+		goto bad;
+
 	if (hashtab_map(p->p_levels.table, validate_level, flavors))
 		goto bad;
 
@@ -707,14 +718,6 @@ bad:
 	return -1;
 }
 
-static int validate_datum(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args)
-{
-	symtab_datum_t *s = d;
-	uint32_t *nprim = (uint32_t *)args;
-
-	return !value_isvalid(s->value, *nprim);
-}
-
 static int validate_symtabs(sepol_handle_t *handle, symtab_t symtabs[], validate_t flavors[])
 {
 	unsigned int i;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 18/35] libsepol: use size_t for indexes in strs helpers
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (16 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 17/35] libsepol: validate types Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 19/35] libsepol: reject abnormal huge sid ids Christian Göttsche
                   ` (18 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Use size_t, as the strs struct uses it for its size member.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_common.c | 8 ++++----
 libsepol/src/kernel_to_common.h | 4 ++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c
index 51df8c25..47c02d61 100644
--- a/libsepol/src/kernel_to_common.c
+++ b/libsepol/src/kernel_to_common.c
@@ -159,7 +159,7 @@ int strs_add(struct strs *strs, char *s)
 {
 	if (strs->num + 1 > strs->size) {
 		char **new;
-		unsigned i = strs->size;
+		size_t i = strs->size;
 		strs->size *= 2;
 		new = reallocarray(strs->list, strs->size, sizeof(char *));
 		if (!new) {
@@ -212,11 +212,11 @@ char *strs_remove_last(struct strs *strs)
 	return strs->list[strs->num];
 }
 
-int strs_add_at_index(struct strs *strs, char *s, unsigned index)
+int strs_add_at_index(struct strs *strs, char *s, size_t index)
 {
 	if (index >= strs->size) {
 		char **new;
-		unsigned i = strs->size;
+		size_t i = strs->size;
 		while (index >= strs->size) {
 			strs->size *= 2;
 		}
@@ -237,7 +237,7 @@ int strs_add_at_index(struct strs *strs, char *s, unsigned index)
 	return 0;
 }
 
-char *strs_read_at_index(struct strs *strs, unsigned index)
+char *strs_read_at_index(struct strs *strs, size_t index)
 {
 	if (index >= strs->num) {
 		return NULL;
diff --git a/libsepol/src/kernel_to_common.h b/libsepol/src/kernel_to_common.h
index 8aa483fa..e9932d30 100644
--- a/libsepol/src/kernel_to_common.h
+++ b/libsepol/src/kernel_to_common.h
@@ -99,8 +99,8 @@ int strs_add(struct strs *strs, char *s);
 __attribute__ ((format(printf, 2, 4)))
 int strs_create_and_add(struct strs *strs, const char *fmt, int num, ...);
 char *strs_remove_last(struct strs *strs);
-int strs_add_at_index(struct strs *strs, char *s, unsigned index);
-char *strs_read_at_index(struct strs *strs, unsigned index);
+int strs_add_at_index(struct strs *strs, char *s, size_t index);
+char *strs_read_at_index(struct strs *strs, size_t index);
 void strs_sort(struct strs *strs);
 unsigned strs_num_items(struct strs *strs);
 size_t strs_len_items(struct strs *strs);
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 19/35] libsepol: reject abnormal huge sid ids
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (17 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 18/35] libsepol: use size_t for indexes in strs helpers Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 20/35] libsepol: do not crash on class gaps Christian Göttsche
                   ` (17 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Check if the sid value is saturated to guard dependent allocations.

    ==19967== ERROR: libFuzzer: out-of-memory (malloc(7784628224))
        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aabe3 in __asan::Allocator::Reallocate(void*, unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aabe3)
        #7 0x4aaa32 in __asan::asan_reallocarray(void*, unsigned long, unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aaa32)
        #8 0x525f8e in __interceptor_reallocarray (./out/binpolicy-fuzzer+0x525f8e)
        #9 0x5ebad3 in strs_add_at_index ./libsepol/src/kernel_to_common.c:224:9
        #10 0x5680eb in write_sids_to_conf ./libsepol/src/kernel_to_conf.c:466:8
        #11 0x55c1c0 in write_sid_decl_rules_to_conf ./libsepol/src/kernel_to_conf.c:498:8
        #12 0x55ad36 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3083:7
        #13 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #14 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #15 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #16 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #17 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #18 0x7f085ac657ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #19 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 5e8b4a3f..51fbd7c8 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -2921,6 +2921,8 @@ static int ocontext_read_xen(const struct policydb_compat_info *info,
 				if (rc < 0)
 					return -1;
 				c->sid[0] = le32_to_cpu(buf[0]);
+				if (is_saturated(c->sid[0]))
+					return -1;
 				if (context_read_and_validate
 				    (&c->context[0], p, fp))
 					return -1;
@@ -3032,6 +3034,8 @@ static int ocontext_read_selinux(const struct policydb_compat_info *info,
 				if (rc < 0)
 					return -1;
 				c->sid[0] = le32_to_cpu(buf[0]);
+				if (is_saturated(c->sid[0]))
+					return -1;
 				if (context_read_and_validate
 				    (&c->context[0], p, fp))
 					return -1;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 20/35] libsepol: do not crash on class gaps
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (18 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 19/35] libsepol: reject abnormal huge sid ids Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 21/35] libsepol: do not crash on user gaps Christian Göttsche
                   ` (16 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Handle gaps in the class table while printing a policy configuration.

    ==21763==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000028 (pc 0x00000055b696 bp 0x7ffe69e8ab50 sp 0x7ffe69e8aa60 T0)
    ==21763==The signal is caused by a READ memory access.
    ==21763==Hint: address points to the zero page.
        #0 0x55b696 in constraint_rules_to_strs ./libsepol/src/kernel_to_conf.c:361:14
        #1 0x55ac80 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3063:7
        #2 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #3 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #4 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #5 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #6 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #7 0x7fc60d39e7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #8 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_cil.c  |  9 +++++++++
 libsepol/src/kernel_to_conf.c | 10 ++++++++--
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
index 305567a5..bb167647 100644
--- a/libsepol/src/kernel_to_cil.c
+++ b/libsepol/src/kernel_to_cil.c
@@ -358,6 +358,7 @@ static int constraint_rules_to_strs(struct policydb *pdb, struct strs *mls_strs,
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->constraints) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_constraint_rules_to_strs(pdb, name, class, class->constraints, mls_strs, non_mls_strs);
@@ -383,6 +384,7 @@ static int validatetrans_rules_to_strs(struct policydb *pdb, struct strs *mls_st
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->validatetrans) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_validatetrans_rules_to_strs(pdb, name, class->validatetrans, mls_strs, non_mls_strs);
@@ -461,6 +463,7 @@ static int write_class_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* class */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = pdb->p_class_val_to_name[i];
 		perms = class_or_common_perms_to_str(&class->permissions);
 		if (perms) {
@@ -488,6 +491,7 @@ static int write_class_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* classcommon */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = pdb->p_class_val_to_name[i];
 		if (class->comkey != NULL) {
 			sepol_printf(out, "(classcommon %s %s)\n", name, class->comkey);
@@ -503,6 +507,7 @@ static int write_class_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 	}
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = class->comkey;
 		if (name != NULL) {
 			common = hashtab_search(pdb->p_commons.table, name);
@@ -727,6 +732,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* default_user */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_user != 0) {
 			rc = write_default_user_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -738,6 +744,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* default_role */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_role != 0) {
 			rc = write_default_role_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -749,6 +756,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* default_type */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_type != 0) {
 			rc = write_default_type_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -764,6 +772,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* default_range */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_range) {
 			rc = write_default_range_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index eb72e4ac..b2a42606 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -358,7 +358,7 @@ static int constraint_rules_to_strs(struct policydb *pdb, struct strs *mls_strs,
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
-		if (class->constraints) {
+		if (class && class->constraints) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_constraint_rules_to_strs(pdb, name, class, class->constraints, mls_strs, non_mls_strs);
 			if (rc != 0) {
@@ -383,7 +383,7 @@ static int validatetrans_rules_to_strs(struct policydb *pdb, struct strs *mls_st
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
-		if (class->validatetrans) {
+		if (class && class->validatetrans) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_validatetrans_rules_to_strs(pdb, name, class->validatetrans, mls_strs, non_mls_strs);
 			if (rc != 0) {
@@ -551,6 +551,7 @@ static int write_class_and_common_rules_to_conf(FILE *out, struct policydb *pdb)
 	}
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = class->comkey;
 		if (!name) continue;
 		common = hashtab_search(pdb->p_commons.table, name);
@@ -577,6 +578,7 @@ static int write_class_and_common_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* class */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = pdb->p_class_val_to_name[i];
 		sepol_printf(out, "class %s", name);
 		if (class->comkey) {
@@ -702,6 +704,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* default_user */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_user != 0) {
 			rc = write_default_user_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -713,6 +716,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* default_role */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_role != 0) {
 			rc = write_default_role_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -724,6 +728,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* default_type */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_type != 0) {
 			rc = write_default_type_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -739,6 +744,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* default_range */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_range != 0) {
 			rc = write_default_range_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 21/35] libsepol: do not crash on user gaps
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (19 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 20/35] libsepol: do not crash on class gaps Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 22/35] libsepol: validate permission count of classes Christian Göttsche
                   ` (15 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Handle gaps in the user table while printing a policy configuration.

    ==24424==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x0000004bdc55 bp 0x7ffc8790b810 sp 0x7ffc8790afb0 T0)
    ==24424==The signal is caused by a READ memory access.
    ==24424==Hint: address points to the zero page.
        #0 0x4bdc55 in __interceptor_strcmp (./out/binpolicy-fuzzer+0x4bdc55)
        #1 0x5ebdf6 in strs_cmp ./libsepol/src/kernel_to_common.c:253:9
        #2 0x505669 in __interceptor_qsort (./out/binpolicy-fuzzer+0x505669)
        #3 0x5ebd84 in strs_sort ./libsepol/src/kernel_to_common.c:261:2
        #4 0x564550 in write_user_decl_rules_to_conf ./libsepol/src/kernel_to_conf.c:2333:2
        #5 0x55b137 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3190:7
        #6 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #7 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #8 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #9 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #10 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #11 0x7f530128d7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #12 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_cil.c  | 1 +
 libsepol/src/kernel_to_conf.c | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
index bb167647..d9dc3f73 100644
--- a/libsepol/src/kernel_to_cil.c
+++ b/libsepol/src/kernel_to_cil.c
@@ -2392,6 +2392,7 @@ static int write_user_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 	}
 
 	for (i=0; i < pdb->p_users.nprim; i++) {
+		if (!pdb->p_user_val_to_name[i]) continue;
 		rc = strs_add(strs, pdb->p_user_val_to_name[i]);
 		if (rc != 0) {
 			goto exit;
diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index b2a42606..68dd2d32 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -2324,6 +2324,7 @@ static int write_user_decl_rules_to_conf(FILE *out, struct policydb *pdb)
 	}
 
 	for (i=0; i < pdb->p_users.nprim; i++) {
+		if (!pdb->p_user_val_to_name[i]) continue;
 		rc = strs_add(strs, pdb->p_user_val_to_name[i]);
 		if (rc != 0) {
 			goto exit;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 22/35] libsepol: validate permission count of classes
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (20 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 21/35] libsepol: do not crash on user gaps Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-13 15:41   ` James Carter
  2021-10-11 16:25 ` [RFC PATCH 23/35] libsepol: resolve log message mismatch Christian Göttsche
                   ` (14 subsequent siblings)
  36 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Check a class has not more than the supported 32 permissions.

    ==28413==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f74ec3341a3 bp 0x7ffd0b7e5030 sp 0x7ffd0b7e47e8 T0)
    ==28413==The signal is caused by a READ memory access.
    ==28413==Hint: address points to the zero page.
        #0 0x7f74ec3341a3  string/../sysdeps/x86_64/multiarch/../strchr.S:32
        #1 0x4bfc78 in strchr (./out/binpolicy-fuzzer+0x4bfc78)
        #2 0x55b7f2 in class_constraint_rules_to_strs ./libsepol/src/kernel_to_conf.c:288:7
        #3 0x55b7f2 in constraint_rules_to_strs ./libsepol/src/kernel_to_conf.c:364:9
        #4 0x55ac80 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3071:7
        #5 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #6 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #7 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #8 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #9 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #10 0x7f74ec2be7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #11 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index c9700399..7ec0675c 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -203,6 +203,8 @@ static int validate_class_datum(sepol_handle_t *handle, class_datum_t *class, va
 		goto bad;
 	if (validate_constraint_nodes(handle, class->validatetrans, flavors))
 		goto bad;
+	if (class->permissions.nprim > PERM_SYMTAB_SIZE)
+		goto bad;
 
 	return 0;
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 23/35] libsepol: resolve log message mismatch
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (21 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 22/35] libsepol: validate permission count of classes Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 24/35] libsepol: zero member before potential dereference Christian Göttsche
                   ` (13 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 7ec0675c..fa128794 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -229,7 +229,7 @@ static int validate_role_datum(sepol_handle_t *handle, role_datum_t *role, valid
 	return 0;
 
 bad:
-	ERR(handle, "Invalid class datum");
+	ERR(handle, "Invalid role datum");
 	return -1;
 }
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 24/35] libsepol: zero member before potential dereference
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (22 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 23/35] libsepol: resolve log message mismatch Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 25/35] libsepol: validate avtab types Christian Göttsche
                   ` (12 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

The `next` member might be checked against NULL and dereferenced before
it gets assigned, due to jumps from failure gotos to the cleanup
section.

    ==31017==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x000000579654 bp 0x7ffd3a07d110 sp 0x7ffd3a07d000 T0)
    ==31017==The signal is caused by a READ memory access.
    ==31017==Hint: this fault was caused by a dereference of a high value address (see register values below).  Disassemble the provided pc to learn which register was used.
        #0 0x579654 in filename_trans_read_one ./libsepol/src/policydb.c:2874:55
        #1 0x579654 in filename_trans_read ./libsepol/src/policydb.c:2902:9
        #2 0x5771b7 in policydb_read ./libsepol/src/policydb.c:4509:7
        #3 0x55a1f5 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:24:6
        #4 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #5 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #6 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #7 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #8 0x7f2a4e7f97ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #9 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 51fbd7c8..d246918b 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -2815,6 +2815,7 @@ static int filename_trans_read_one(policydb_t *p, struct policy_file *fp)
 		if (!datum)
 			goto err;
 
+		datum->next = NULL;
 		*dst = datum;
 
 		/* ebitmap_read() will at least init the bitmap */
@@ -2832,7 +2833,6 @@ static int filename_trans_read_one(policydb_t *p, struct policy_file *fp)
 
 		dst = &datum->next;
 	}
-	*dst = NULL;
 
 	if (ndatum > 1 && filename_trans_check_datum(first))
 		goto err;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 25/35] libsepol: validate avtab types
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (23 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 24/35] libsepol: zero member before potential dereference Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-18 19:54   ` James Carter
  2021-10-11 16:25 ` [RFC PATCH 26/35] libsepol: validate constraint expression operators and attributes Christian Göttsche
                   ` (11 subsequent siblings)
  36 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Check for invalid avtab types.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index fa128794..89830ff3 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -441,6 +441,20 @@ static int validate_avtab_key(avtab_key_t *key, validate_t flavors[])
 		goto bad;
 	if (validate_value(key->target_class, &flavors[SYM_CLASSES]))
 		goto bad;
+	switch (0xFFF & key->specified) {
+	case AVTAB_ALLOWED:
+	case AVTAB_AUDITALLOW:
+	case AVTAB_AUDITDENY:
+	case AVTAB_XPERMS_ALLOWED:
+	case AVTAB_XPERMS_AUDITALLOW:
+	case AVTAB_XPERMS_DONTAUDIT:
+	case AVTAB_TRANSITION:
+	case AVTAB_MEMBER:
+	case AVTAB_CHANGE:
+		break;
+	default:
+		goto bad;
+	}
 
 	return 0;
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 26/35] libsepol: validate constraint expression operators and attributes
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (24 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 25/35] libsepol: validate avtab types Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 27/35] libsepol: validate type of avtab type rules Christian Göttsche
                   ` (10 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 43 ++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 89830ff3..f0456583 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -185,6 +185,49 @@ static int validate_constraint_nodes(sepol_handle_t *handle, constraint_node_t *
 				if (validate_type_set(cexp->type_names, &flavors[SYM_TYPES]))
 					goto bad;
 			}
+
+			if (cexp->expr_type == CEXPR_ATTR || cexp->expr_type == CEXPR_NAMES) {
+				switch (cexp->op) {
+				case CEXPR_EQ:
+				case CEXPR_NEQ:
+				case CEXPR_DOM:
+				case CEXPR_DOMBY:
+				case CEXPR_INCOMP:
+					break;
+				default:
+					goto bad;
+				}
+
+				switch (cexp->attr) {
+				case CEXPR_USER:
+				case CEXPR_USER | CEXPR_TARGET:
+				case CEXPR_USER | CEXPR_XTARGET:
+				case CEXPR_ROLE:
+				case CEXPR_ROLE | CEXPR_TARGET:
+				case CEXPR_ROLE | CEXPR_XTARGET:
+				case CEXPR_TYPE:
+				case CEXPR_TYPE | CEXPR_TARGET:
+				case CEXPR_TYPE | CEXPR_XTARGET:
+				case CEXPR_L1L2:
+				case CEXPR_L1H2:
+				case CEXPR_H1L2:
+				case CEXPR_H1H2:
+				case CEXPR_L1H1:
+				case CEXPR_L2H2:
+					break;
+				default:
+					goto bad;
+				}
+			} else {
+				switch (cexp->expr_type) {
+				case CEXPR_NOT:
+				case CEXPR_AND:
+				case CEXPR_OR:
+					break;
+				default:
+					goto bad;
+				}
+			}
 		}
 	}
 
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 27/35] libsepol: validate type of avtab type rules
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (25 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 26/35] libsepol: validate constraint expression operators and attributes Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-13 15:44   ` James Carter
  2021-10-11 16:25 ` [RFC PATCH 28/35] libsepol: validate ocontexts Christian Göttsche
                   ` (9 subsequent siblings)
  36 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

    ==80903==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000005c0 at pc 0x0000005696c8 bp 0x7ffdb11ea560 sp 0x7ffdb11ea558
    READ of size 8 at 0x6020000005c0 thread T0
        #0 0x5696c7 in avtab_node_to_str ./libsepol/src/kernel_to_conf.c:1736:9
        #1 0x569013 in map_avtab_write_helper ./libsepol/src/kernel_to_conf.c:1767:10
        #2 0x5ab837 in avtab_map ./libsepol/src/avtab.c:347:10
        #3 0x561f9a in write_avtab_flavor_to_conf ./libsepol/src/kernel_to_conf.c:1798:7
        #4 0x561f9a in write_avtab_to_conf ./libsepol/src/kernel_to_conf.c:1819:8
        #5 0x55afba in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3159:7
        #6 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #7 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #8 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #9 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #10 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #11 0x7f97a83fd7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #12 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index f0456583..9134e541 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -505,15 +505,22 @@ bad:
 	return -1;
 }
 
-static int validate_avtab_key_wrapper(avtab_key_t *k,  __attribute__ ((unused)) avtab_datum_t *d, void *args)
+static int validate_avtab(avtab_key_t *k, avtab_datum_t *d, void *args)
 {
 	validate_t *flavors = (validate_t *)args;
-	return validate_avtab_key(k, flavors);
+
+	if (validate_avtab_key(k, flavors))
+		return -1;
+
+	if ((k->specified & AVTAB_TYPE) && validate_value(d->data, &flavors[SYM_TYPES]))
+		return -1;
+
+	return 0;
 }
 
-static int validate_avtab(sepol_handle_t *handle, avtab_t *avtab, validate_t flavors[])
+static int validate_avtabs(sepol_handle_t *handle, avtab_t *avtab, validate_t flavors[])
 {
-	if (avtab_map(avtab, validate_avtab_key_wrapper, flavors)) {
+	if (avtab_map(avtab, validate_avtab, flavors)) {
 		ERR(handle, "Invalid avtab");
 		return -1;
 	}
@@ -845,7 +852,7 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 		goto bad;
 
 	if (p->policy_type == POLICY_KERN) {
-		if (validate_avtab(handle, &p->te_avtab, flavors))
+		if (validate_avtabs(handle, &p->te_avtab, flavors))
 			goto bad;
 		if (p->policyvers >= POLICYDB_VERSION_BOOL)
 			if (validate_cond_list(handle, p->cond_list, flavors))
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 28/35] libsepol: validate ocontexts
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (26 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 27/35] libsepol: validate type of avtab type rules Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-14 14:10   ` James Carter
  2021-10-11 16:25 ` [RFC PATCH 29/35] libsepol: validate genfs contexts Christian Göttsche
                   ` (8 subsequent siblings)
  36 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

    ==91274==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f60b0afe8c6 bp 0x7ffd42edc990 sp 0x7ffd42edc148 T0)
    ==91274==The signal is caused by a READ memory access.
    ==91274==Hint: address points to the zero page.
        #0 0x7f60b0afe8c6  string/../sysdeps/x86_64/multiarch/../strlen.S:120
        #1 0x4bd128 in __interceptor_strlen (./out/binpolicy-fuzzer+0x4bd128)
        #2 0x5eb387 in create_str_helper ./libsepol/src/kernel_to_common.c:69:10
        #3 0x5eb11e in create_str ./libsepol/src/kernel_to_common.c:99:8
        #4 0x56ad7b in context_to_str ./libsepol/src/kernel_to_conf.c:2408:9
        #5 0x56a717 in write_sid_context_rules_to_conf ./libsepol/src/kernel_to_conf.c:2441:9
        #6 0x55b26c in write_selinux_isid_rules_to_conf ./libsepol/src/kernel_to_conf.c:2476:9
        #7 0x55b26c in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3206:8
        #8 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #9 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #10 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #11 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #12 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #13 0x7f60b0a887ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #14 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 37 ++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 9134e541..5c06e6f4 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -677,6 +677,41 @@ static int validate_filename_trans_hashtab(sepol_handle_t *handle, hashtab_t fil
 	return 0;
 }
 
+static int validate_context(context_struct_t *con, validate_t flavors[], int mls)
+{
+	if (validate_value(con->user, &flavors[SYM_USERS]))
+		return -1;
+	if (validate_value(con->role, &flavors[SYM_ROLES]))
+		return -1;
+	if (validate_value(con->type, &flavors[SYM_TYPES]))
+		return -1;
+	if (mls && validate_mls_range(&con->range, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		return -1;
+
+	return 0;
+}
+
+static int validate_ocontexts(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	ocontext_t *octx;
+	unsigned int i;
+
+	for (i = 0; i < OCON_NUM; i++) {
+		for (octx = p->ocontexts[i]; octx; octx = octx->next) {
+			if (validate_context(&octx->context[0], flavors, p->mls))
+				goto bad;
+			if ((i == OCON_FS || i == OCON_NETIF) && validate_context(&octx->context[1], flavors, p->mls))
+				goto bad;
+		}
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid ocontext");
+	return -1;
+}
+
 /*
  * Functions to validate a module policydb
  */
@@ -861,6 +896,8 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 			goto bad;
 		if (validate_role_allows(handle, p->role_allow, flavors))
 			goto bad;
+		if (validate_ocontexts(handle, p, flavors))
+			goto bad;
 		if (p->policyvers >= POLICYDB_VERSION_FILENAME_TRANS)
 			if (validate_filename_trans_hashtab(handle, p->filename_trans, flavors))
 				goto bad;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 29/35] libsepol: validate genfs contexts
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (27 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 28/35] libsepol: validate ocontexts Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-14 14:10   ` James Carter
  2021-10-11 16:25 ` [RFC PATCH 30/35] libsepol: validate permissive types Christian Göttsche
                   ` (7 subsequent siblings)
  36 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 5c06e6f4..63fd935c 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -712,6 +712,25 @@ bad:
 	return -1;
 }
 
+static int validate_genfs(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	genfs_t *genfs;
+	ocontext_t *octx;
+
+	for (genfs = p->genfs; genfs; genfs = genfs->next) {
+		for (octx = genfs->head; octx; octx = octx->next) {
+			if (validate_context(&octx->context[0], flavors, p->mls))
+				goto bad;
+		}
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid genfs");
+	return -1;
+}
+
 /*
  * Functions to validate a module policydb
  */
@@ -898,6 +917,8 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 			goto bad;
 		if (validate_ocontexts(handle, p, flavors))
 			goto bad;
+		if (validate_genfs(handle, p, flavors))
+			goto bad;
 		if (p->policyvers >= POLICYDB_VERSION_FILENAME_TRANS)
 			if (validate_filename_trans_hashtab(handle, p->filename_trans, flavors))
 				goto bad;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 30/35] libsepol: validate permissive types
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (28 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 29/35] libsepol: validate genfs contexts Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 31/35] libsepol: validate policy properties Christian Göttsche
                   ` (6 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 63fd935c..b1dacdad 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -886,6 +886,23 @@ bad:
 	return -1;
 }
 
+static int validate_permissives(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	ebitmap_node_t *node;
+	unsigned i;
+
+	ebitmap_for_each_positive_bit(&p->permissive_map, node, i) {
+		if (validate_value(i, &flavors[SYM_TYPES]))
+			goto bad;
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid permissive type");
+	return -1;
+}
+
 static void validate_array_destroy(validate_t flavors[])
 {
 	unsigned int i;
@@ -933,6 +950,9 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 	if (validate_datum_arrays(handle, p, flavors))
 		goto bad;
 
+	if (validate_permissives(handle, p, flavors))
+		goto bad;
+
 	validate_array_destroy(flavors);
 
 	return 0;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 31/35] libsepol: validate policy properties
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (29 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 30/35] libsepol: validate permissive types Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 32/35] libsepol: do not underflow on short format arguments Christian Göttsche
                   ` (5 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 51 ++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index b1dacdad..860f9647 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -903,6 +903,54 @@ bad:
 	return -1;
 }
 
+static int validate_properties(sepol_handle_t *handle, policydb_t *p)
+{
+	switch (p->policy_type) {
+	case POLICY_KERN:
+		if (p->policyvers < POLICYDB_VERSION_MIN || p->policyvers > POLICYDB_VERSION_MAX)
+			goto bad;
+		break;
+	case POLICY_BASE:
+	case POLICY_MOD:
+		if (p->policyvers < MOD_POLICYDB_VERSION_MIN || p->policyvers > MOD_POLICYDB_VERSION_MAX)
+			goto bad;
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (p->target_platform) {
+	case SEPOL_TARGET_SELINUX:
+	case SEPOL_TARGET_XEN:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (p->mls) {
+	case 0:
+	case 1:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (p->handle_unknown) {
+	case SEPOL_DENY_UNKNOWN:
+	case SEPOL_REJECT_UNKNOWN:
+	case SEPOL_ALLOW_UNKNOWN:
+		break;
+	default:
+		goto bad;
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid policy property");
+	return -1;
+}
+
 static void validate_array_destroy(validate_t flavors[])
 {
 	unsigned int i;
@@ -922,6 +970,9 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 	if (validate_array_init(p, flavors))
 		goto bad;
 
+	if (validate_properties(handle, p))
+		goto bad;
+
 	if (p->policy_type == POLICY_KERN) {
 		if (validate_avtabs(handle, &p->te_avtab, flavors))
 			goto bad;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 32/35] libsepol: do not underflow on short format arguments
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (30 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 31/35] libsepol: validate policy properties Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 33/35] libsepol: validate categories Christian Göttsche
                   ` (4 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Handle format arguments that do not have a size of at least 2.

    kernel_to_common.c:69:20: runtime error: unsigned integer overflow: 1 - 2 cannot be represented in type 'unsigned long'
        #0 0x557b0b in create_str_helper ./libsepol/src/kernel_to_common.c:69:20
        #1 0x5577b8 in create_str ./libsepol/src/kernel_to_common.c:99:8
        #2 0x56448c in cond_expr_to_str ./libsepol/src/kernel_to_conf.c:82:15
        #3 0x56448c in write_cond_nodes_to_conf ./libsepol/src/kernel_to_conf.c:2103:10
        #4 0x55bd9b in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3171:7
        #5 0x4f9d79 in main ./checkpolicy/checkpolicy.c:684:11
        #6 0x7fe2a342b7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #7 0x41f3a9 in _start (./checkpolicy/checkpolicy+0x41f3a9)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_common.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c
index 47c02d61..152f2816 100644
--- a/libsepol/src/kernel_to_common.c
+++ b/libsepol/src/kernel_to_common.c
@@ -57,7 +57,7 @@ static char *create_str_helper(const char *fmt, int num, va_list vargs)
 	va_list vargs2;
 	char *str = NULL;
 	char *s;
-	size_t len;
+	size_t len, s_len;
 	int i, rc;
 
 	va_copy(vargs2, vargs);
@@ -66,7 +66,8 @@ static char *create_str_helper(const char *fmt, int num, va_list vargs)
 
 	for (i=0; i<num; i++) {
 		s = va_arg(vargs, char *);
-		len += strlen(s) - 2; /* -2 for each %s in fmt */
+		s_len = strlen(s);
+		len += s_len > 1 ? s_len - 2 : 0; /* -2 for each %s in fmt */
 	}
 
 	str = malloc(len);
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 33/35] libsepol: validate categories
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (31 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 32/35] libsepol: do not underflow on short format arguments Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-13 15:40   ` James Carter
  2021-10-11 16:25 ` [RFC PATCH 34/35] libsepol: use correct size for initial string list Christian Göttsche
                   ` (3 subsequent siblings)
  36 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Check all categories have valid values, especially important for
aliases.

        ==7888==ERROR: AddressSanitizer: SEGV on unknown address 0x602000400710 (pc 0x00000055debc bp 0x7ffe0ff2a9d0 sp 0x7ffe0ff2a8e0 T0)
        ==7888==The signal is caused by a READ memory access.
        #0 0x55debc in write_category_rules_to_conf ./libsepol/src/kernel_to_conf.c:946:9
        #1 0x55debc in write_mls_rules_to_conf ./libsepol/src/kernel_to_conf.c:1137:7
        #2 0x55adb1 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3106:7
        #3 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:37:9
        #4 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #5 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #6 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #7 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #8 0x7fe80ccaf7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #9 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 860f9647..063bde18 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -465,6 +465,9 @@ static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate
 	if (hashtab_map(p->p_levels.table, validate_level, flavors))
 		goto bad;
 
+	if (hashtab_map(p->p_cats.table, validate_datum, &flavors[SYM_CATS]))
+		goto bad;
+
 	return 0;
 
 bad:
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 34/35] libsepol: use correct size for initial string list
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (32 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 33/35] libsepol: validate categories Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-11 16:25 ` [RFC PATCH 35/35] libsepol: do not create a string list with initial size zero Christian Göttsche
                   ` (2 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Use the number of categories not levels, which might be zero, for the
string list initial size of categories.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_conf.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index 68dd2d32..dcdd4252 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -914,7 +914,7 @@ static int write_category_rules_to_conf(FILE *out, struct policydb *pdb)
 	unsigned i, j, num;
 	int rc = 0;
 
-	rc = strs_init(&strs, pdb->p_levels.nprim);
+	rc = strs_init(&strs, pdb->p_cats.nprim);
 	if (rc != 0) {
 		goto exit;
 	}
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH 35/35] libsepol: do not create a string list with initial size zero
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (33 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 34/35] libsepol: use correct size for initial string list Christian Göttsche
@ 2021-10-11 16:25 ` Christian Göttsche
  2021-10-13 14:07 ` [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies James Carter
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-10-11 16:25 UTC (permalink / raw)
  To: selinux

Currently is it implementation defined, due to the size being passed to
calloc(3), whether the operations fails nor not.
Also strs_add() does not handle a size of zero, cause it just multiplies
the size by two.

Use a default size of 1 if 0 is passed and swap the calloc arguments for
consistency.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_common.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c
index 152f2816..9f5400c9 100644
--- a/libsepol/src/kernel_to_common.c
+++ b/libsepol/src/kernel_to_common.c
@@ -107,6 +107,10 @@ int strs_init(struct strs **strs, size_t size)
 {
 	struct strs *new;
 
+	if (size == 0) {
+		size = 1;
+	}
+
 	*strs = NULL;
 
 	new = malloc(sizeof(struct strs));
@@ -115,7 +119,7 @@ int strs_init(struct strs **strs, size_t size)
 		return -1;
 	}
 
-	new->list = calloc(sizeof(char *), size);
+	new->list = calloc(size, sizeof(char *));
 	if (!new->list) {
 		sepol_log_err("Out of memory");
 		free(new);
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (34 preceding siblings ...)
  2021-10-11 16:25 ` [RFC PATCH 35/35] libsepol: do not create a string list with initial size zero Christian Göttsche
@ 2021-10-13 14:07 ` James Carter
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
  36 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-10-13 14:07 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

This needs to be broken up into three patch sets.

1) libfuzz: patches 1-5
2) policydb_validate improvements: patches 13, 16, 17, 22, 23, 25-31, 33
3) General libsepol fixes: patches 6-12, 14, 15, 18-21, 24, 32, 34, 35

I have some comments on the individual patches which will be coming soon.

Thanks,
Jim

On Mon, Oct 11, 2021 at 12:42 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Add a libfuzz[1] based fuzzer testing the reading and parsing of binary policy
> files. This fuzzer will be run within the OSS-Fuzz service.
>
> Handle and reject a variety of edge cases causing crashes or resource leaks.
>
> The fifth patch ("libsepol/fuzz: limit element sizes for fuzzing") needs some
> discussion: To avoid oom reports from the fuzzer, caused by huge memory
> allocations, all identifiers are limited to a length of 2^16 for the fuzzer
> build only.  Probably there should be a limit for the release build too.
> Is there a specification for the binary policy format saying something about
> the maximum length of identifiers?
> After a quick look at the kernel sources (most interesting is str_read()) I
> could not find any limits either.
>
> [1]: https://llvm.org/docs/LibFuzzer.html
>
> Christian Göttsche (35):
>   cifuzz: enable report-unreproducible-crashes
>   cifuzz: use the default runtime of 600 seconds
>   libsepol/fuzz: silence secilc-fuzzer
>   libsepol: add libfuzz based fuzzer for reading binary policies
>   libsepol/fuzz: limit element sizes for fuzzing
>   libsepol: use logging framework in conditional.c
>   libsepol: use logging framework in ebitmap.c
>   libsepol: use mallocarray wrapper to avoid overflows
>   libsepol: use reallocarray wrapper to avoid overflows
>   libsepol: add checks for read sizes
>   libsepol: enforce avtab item limit
>   libsepol: clean memory on conditional read failure
>   libsepol: validate MLS levels
>   libsepol: reject invalid fsuse types
>   libsepol: reject invalid default targets
>   libsepol: validate expanded user range and level
>   libsepol: validate types
>   libsepol: use size_t for indexes in strs helpers
>   libsepol: reject abnormal huge sid ids
>   libsepol: do not crash on class gaps
>   libsepol: do not crash on user gaps
>   libsepol: validate permission count of classes
>   libsepol: resolve log message mismatch
>   libsepol: zero member before potential dereference
>   libsepol: validate avtab types
>   libsepol: validate constraint expression operators and attributes
>   libsepol: validate type of avtab type rules
>   libsepol: validate ocontexts
>   libsepol: validate genfs contexts
>   libsepol: validate permissive types
>   libsepol: validate policy properties
>   libsepol: do not underflow on short format arguments
>   libsepol: validate categories
>   libsepol: use correct size for initial string list
>   libsepol: do not create a string list with initial size zero
>
>  .github/workflows/cifuzz.yml     |   3 +-
>  libsepol/fuzz/binpolicy-fuzzer.c |  63 +++++++
>  libsepol/fuzz/policy.bin         | Bin 0 -> 1552 bytes
>  libsepol/fuzz/secilc-fuzzer.c    |   5 +
>  libsepol/src/Makefile            |   6 +
>  libsepol/src/avtab.c             |   6 +
>  libsepol/src/conditional.c       |  36 ++--
>  libsepol/src/ebitmap.c           |  27 ++-
>  libsepol/src/expand.c            |   4 +-
>  libsepol/src/hashtab.c           |   4 +-
>  libsepol/src/kernel_to_cil.c     |  10 ++
>  libsepol/src/kernel_to_common.c  |  23 ++-
>  libsepol/src/kernel_to_common.h  |   4 +-
>  libsepol/src/kernel_to_conf.c    |  13 +-
>  libsepol/src/link.c              |   3 +-
>  libsepol/src/module.c            |   4 +-
>  libsepol/src/module_to_cil.c     |  13 +-
>  libsepol/src/optimize.c          |  11 +-
>  libsepol/src/policydb.c          |  68 +++++++-
>  libsepol/src/policydb_validate.c | 274 +++++++++++++++++++++++++++++--
>  libsepol/src/private.h           |  27 ++-
>  libsepol/src/services.c          |  12 +-
>  libsepol/src/sidtab.c            |   3 +-
>  libsepol/src/user_record.c       |   8 +-
>  libsepol/src/users.c             |  12 +-
>  libsepol/src/util.c              |  11 +-
>  libsepol/src/write.c             |   2 +-
>  scripts/oss-fuzz.sh              |  19 ++-
>  28 files changed, 556 insertions(+), 115 deletions(-)
>  create mode 100644 libsepol/fuzz/binpolicy-fuzzer.c
>  create mode 100644 libsepol/fuzz/policy.bin
>
> --
> 2.33.0
>

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH 12/35] libsepol: clean memory on conditional read failure
  2021-10-11 16:25 ` [RFC PATCH 12/35] libsepol: clean memory on conditional read failure Christian Göttsche
@ 2021-10-13 14:10   ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-10-13 14:10 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Mon, Oct 11, 2021 at 12:41 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Free the local access vector list on failure as it does not get moved
> into the policy structure.
>
>     Direct leak of 16 byte(s) in 1 object(s) allocated from:
>         #0 0x52596d in malloc (./out/binpolicy-fuzzer+0x52596d)
>         #1 0x5b30d2 in cond_insertf ./libsepol/src/conditional.c:682:9
>         #2 0x5ac218 in avtab_read_item ./libsepol/src/avtab.c:583:10
>         #3 0x5b21f4 in cond_read_av_list ./libsepol/src/conditional.c:725:8
>         #4 0x5b21f4 in cond_read_node ./libsepol/src/conditional.c:798:7
>         #5 0x5b21f4 in cond_read_list ./libsepol/src/conditional.c:847:7
>         #6 0x576b6e in policydb_read ./libsepol/src/policydb.c:4436:8
>         #7 0x55a1fe in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:24:6
>         #8 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
>         #9 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
>         #10 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
>         #11 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
>         #12 0x7f47abeb87ec in __libc_start_main csu/../csu/libc-start.c:332:16
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/src/conditional.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c
> index 9a10aae1..50cb5395 100644
> --- a/libsepol/src/conditional.c
> +++ b/libsepol/src/conditional.c
> @@ -724,8 +724,10 @@ static int cond_read_av_list(policydb_t * p, void *fp,
>         for (i = 0; i < len; i++) {
>                 rc = avtab_read_item(fp, p->policyvers, &p->te_cond_avtab,
>                                      cond_insertf, &data);
> -               if (rc)
> +               if (rc) {
> +                       cond_av_list_destroy(data.head);
>                         return rc;
> +               }
>
>         }
>
> --
> 2.33.0
>

Please remove the cond_av_list_destroy() call in cond_insertf(). That
won't be needed with it being called here.

Jim

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH 13/35] libsepol: validate MLS levels
  2021-10-11 16:25 ` [RFC PATCH 13/35] libsepol: validate MLS levels Christian Göttsche
@ 2021-10-13 15:38   ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-10-13 15:38 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Mon, Oct 11, 2021 at 12:41 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Validate the level map of the policy to ensure no level refers to a non
> existent category.
>
> READ of size 8 at 0x602000000c58 thread T0
>     #0 0x568d2c in cats_ebitmap_len ./libsepol/src/kernel_to_conf.c:1003:14
>     #1 0x568d2c in cats_ebitmap_to_str ./libsepol/src/kernel_to_conf.c:1038:19
>     #2 0x55e371 in write_level_rules_to_conf ./libsepol/src/kernel_to_conf.c:1106:11
>     #3 0x55e371 in write_mls_rules_to_conf ./libsepol/src/kernel_to_conf.c:1140:7
>     #4 0x55adb1 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3103:7
>     #5 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
>     #6 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
>     #7 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
>     #8 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
>     #9 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
>     #10 0x7f741d0d67ec in __libc_start_main csu/../csu/libc-start.c:332:16
>     #11 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/src/policydb_validate.c | 26 ++++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
>
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index 5804d247..ca0dcca3 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -310,6 +310,29 @@ bad:
>         return -1;
>  }
>
> +static int validate_mls_level(mls_level_t *level, validate_t *sens, validate_t *cats)
> +{
> +       if (level->sens == 0)
> +               return 0;
> +       if (validate_value(level->sens, sens))
> +               goto bad;
> +       if (validate_ebitmap(&level->cat, cats))
> +               goto bad;
> +
> +       return 0;
> +
> +       bad:
> +       return -1;
> +}
> +
> +static int validate_level(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args)

I would prefer validate_level_datum for the name of this function.

> +{
> +       level_datum_t *level = d;
> +       validate_t *flavors = args;
> +
> +       return validate_mls_level(level->level, &flavors[SYM_LEVELS], &flavors[SYM_CATS]);
> +}
> +
>  static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
>  {
>         unsigned int i;
> @@ -368,6 +391,9 @@ static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate
>                 }
>         }
>
> +       if (hashtab_map(p->p_levels.table, validate_level, flavors))
> +               goto bad;
> +

This should not be in validate_datum_arrays(). This also applies to
patches 17 and 33.
I think the best course would be to pull out the validate_X_datum()
calls out of validate_datum_arrays() so that the only thing being
checked is that the gaps in the X_val_to_struct arrays correspond to
any gaps in the X_val_to_name arrays.

Have a function called validate_global_symtabs() or something that
walks all of the symtabs and does the checks on the datums (including
aliases). So this hashtab_map() call would be in the function, along
with calls to hashtab_map using modified validate_X_datum() functions
that can be used with hashtab_map().

I think something like:

if (hashtab_map(p->p_commons.table, validate_common_datum, flavors))
            goto bad;
if (hashtab_map(p->p_classes.table, validate_class_datum, flavors))
            goto bad;
if (hashtab_map(p->p_roles.table, validate_role_datum, flavors))
            goto bad;
if (hashtab_map(p->p_types.table, validate_type_datum, flavors))
            goto bad;
if (hashtab_map(p->p_users.table, validate_user_datum, flavors))
            goto bad;
if (hashtab_map(p->p_bools.table, validate_bool_datum, flavors))
            goto bad;
if (hashtab_map(p->p_levels.table, validate_level_datum, flavors))
            goto bad;
if (hashtab_map(p->p_cats.table, validate_cat_datum, flavors))
            goto bad;

Thanks,
Jim

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH 17/35] libsepol: validate types
  2021-10-11 16:25 ` [RFC PATCH 17/35] libsepol: validate types Christian Göttsche
@ 2021-10-13 15:39   ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-10-13 15:39 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Mon, Oct 11, 2021 at 12:41 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Check all types are valid values, especially important for aliases.
>
>     ==9702==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000af8 at pc 0x000000560698 bp 0x7ffcca93b9f0 sp 0x7ffcca93b9e8
>     READ of size 8 at 0x602000000af8 thread T0
>         #0 0x560697 in write_type_alias_rules_to_conf ./libsepol/src/kernel_to_conf.c:1424:10
>         #1 0x55af16 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3131:7
>         #2 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
>         #3 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
>         #4 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
>         #5 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
>         #6 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
>         #7 0x7f518b1d57ec in __libc_start_main csu/../csu/libc-start.c:332:16
>         #8 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/src/policydb_validate.c | 19 +++++++++++--------
>  1 file changed, 11 insertions(+), 8 deletions(-)
>
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index a6ae728a..c9700399 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -348,6 +348,14 @@ static int validate_level(__attribute__ ((unused))hashtab_key_t k, hashtab_datum
>         return validate_mls_level(level->level, &flavors[SYM_LEVELS], &flavors[SYM_CATS]);
>  }
>
> +static int validate_datum(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args)
> +{
> +       symtab_datum_t *s = d;
> +       uint32_t *nprim = (uint32_t *)args;
> +
> +       return !value_isvalid(s->value, *nprim);
> +}
> +
>  static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
>  {
>         unsigned int i;
> @@ -406,6 +414,9 @@ static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate
>                 }
>         }
>
> +       if (hashtab_map(p->p_types.table, validate_datum, &flavors[SYM_TYPES]))
> +               goto bad;
> +
>         if (hashtab_map(p->p_levels.table, validate_level, flavors))
>                 goto bad;
>

This should not be in this function. See the comments for patch 13.

Thanks,
Jim


> @@ -707,14 +718,6 @@ bad:
>         return -1;
>  }
>
> -static int validate_datum(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args)
> -{
> -       symtab_datum_t *s = d;
> -       uint32_t *nprim = (uint32_t *)args;
> -
> -       return !value_isvalid(s->value, *nprim);
> -}
> -
>  static int validate_symtabs(sepol_handle_t *handle, symtab_t symtabs[], validate_t flavors[])
>  {
>         unsigned int i;
> --
> 2.33.0
>

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH 33/35] libsepol: validate categories
  2021-10-11 16:25 ` [RFC PATCH 33/35] libsepol: validate categories Christian Göttsche
@ 2021-10-13 15:40   ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-10-13 15:40 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Mon, Oct 11, 2021 at 12:41 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Check all categories have valid values, especially important for
> aliases.
>
>         ==7888==ERROR: AddressSanitizer: SEGV on unknown address 0x602000400710 (pc 0x00000055debc bp 0x7ffe0ff2a9d0 sp 0x7ffe0ff2a8e0 T0)
>         ==7888==The signal is caused by a READ memory access.
>         #0 0x55debc in write_category_rules_to_conf ./libsepol/src/kernel_to_conf.c:946:9
>         #1 0x55debc in write_mls_rules_to_conf ./libsepol/src/kernel_to_conf.c:1137:7
>         #2 0x55adb1 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3106:7
>         #3 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:37:9
>         #4 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
>         #5 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
>         #6 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
>         #7 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
>         #8 0x7fe80ccaf7ec in __libc_start_main csu/../csu/libc-start.c:332:16
>         #9 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/src/policydb_validate.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index 860f9647..063bde18 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -465,6 +465,9 @@ static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate
>         if (hashtab_map(p->p_levels.table, validate_level, flavors))
>                 goto bad;
>
> +       if (hashtab_map(p->p_cats.table, validate_datum, &flavors[SYM_CATS]))
> +               goto bad;
> +

This should not be in this function. See the comments on patch 13.

Thanks,
Jim


>         return 0;
>
>  bad:
> --
> 2.33.0
>

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH 22/35] libsepol: validate permission count of classes
  2021-10-11 16:25 ` [RFC PATCH 22/35] libsepol: validate permission count of classes Christian Göttsche
@ 2021-10-13 15:41   ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-10-13 15:41 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Mon, Oct 11, 2021 at 12:41 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Check a class has not more than the supported 32 permissions.
>
>     ==28413==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f74ec3341a3 bp 0x7ffd0b7e5030 sp 0x7ffd0b7e47e8 T0)
>     ==28413==The signal is caused by a READ memory access.
>     ==28413==Hint: address points to the zero page.
>         #0 0x7f74ec3341a3  string/../sysdeps/x86_64/multiarch/../strchr.S:32
>         #1 0x4bfc78 in strchr (./out/binpolicy-fuzzer+0x4bfc78)
>         #2 0x55b7f2 in class_constraint_rules_to_strs ./libsepol/src/kernel_to_conf.c:288:7
>         #3 0x55b7f2 in constraint_rules_to_strs ./libsepol/src/kernel_to_conf.c:364:9
>         #4 0x55ac80 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3071:7
>         #5 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
>         #6 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
>         #7 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
>         #8 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
>         #9 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
>         #10 0x7f74ec2be7ec in __libc_start_main csu/../csu/libc-start.c:332:16
>         #11 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/src/policydb_validate.c | 2 ++
>  1 file changed, 2 insertions(+)
>
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index c9700399..7ec0675c 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -203,6 +203,8 @@ static int validate_class_datum(sepol_handle_t *handle, class_datum_t *class, va
>                 goto bad;
>         if (validate_constraint_nodes(handle, class->validatetrans, flavors))
>                 goto bad;
> +       if (class->permissions.nprim > PERM_SYMTAB_SIZE)
> +               goto bad;
>

This is good, but it also needs to be done for commons. See comments
for patch 13.

Thanks,
Jim


>         return 0;
>
> --
> 2.33.0
>

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH 27/35] libsepol: validate type of avtab type rules
  2021-10-11 16:25 ` [RFC PATCH 27/35] libsepol: validate type of avtab type rules Christian Göttsche
@ 2021-10-13 15:44   ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-10-13 15:44 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Mon, Oct 11, 2021 at 12:41 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
>     ==80903==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000005c0 at pc 0x0000005696c8 bp 0x7ffdb11ea560 sp 0x7ffdb11ea558
>     READ of size 8 at 0x6020000005c0 thread T0
>         #0 0x5696c7 in avtab_node_to_str ./libsepol/src/kernel_to_conf.c:1736:9
>         #1 0x569013 in map_avtab_write_helper ./libsepol/src/kernel_to_conf.c:1767:10
>         #2 0x5ab837 in avtab_map ./libsepol/src/avtab.c:347:10
>         #3 0x561f9a in write_avtab_flavor_to_conf ./libsepol/src/kernel_to_conf.c:1798:7
>         #4 0x561f9a in write_avtab_to_conf ./libsepol/src/kernel_to_conf.c:1819:8
>         #5 0x55afba in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3159:7
>         #6 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
>         #7 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
>         #8 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
>         #9 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
>         #10 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
>         #11 0x7f97a83fd7ec in __libc_start_main csu/../csu/libc-start.c:332:16
>         #12 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/src/policydb_validate.c | 17 ++++++++++++-----
>  1 file changed, 12 insertions(+), 5 deletions(-)
>
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index f0456583..9134e541 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -505,15 +505,22 @@ bad:
>         return -1;
>  }
>
> -static int validate_avtab_key_wrapper(avtab_key_t *k,  __attribute__ ((unused)) avtab_datum_t *d, void *args)
> +static int validate_avtab(avtab_key_t *k, avtab_datum_t *d, void *args)
>  {

I don't like the function name (not that I liked the old one either).
I think validate_avtab_key_and_datum() would be better.

>         validate_t *flavors = (validate_t *)args;
> -       return validate_avtab_key(k, flavors);
> +
> +       if (validate_avtab_key(k, flavors))
> +               return -1;
> +
> +       if ((k->specified & AVTAB_TYPE) && validate_value(d->data, &flavors[SYM_TYPES]))
> +               return -1;
> +
> +       return 0;
>  }
>
> -static int validate_avtab(sepol_handle_t *handle, avtab_t *avtab, validate_t flavors[])
> +static int validate_avtabs(sepol_handle_t *handle, avtab_t *avtab, validate_t flavors[])

I don't like this function name at all, because only one avtab is
being checked. With the name change for the function above, you can
leave this as validate_avtab().

Everything else about this patch looks good.

Thanks,
Jim

>  {
> -       if (avtab_map(avtab, validate_avtab_key_wrapper, flavors)) {
> +       if (avtab_map(avtab, validate_avtab, flavors)) {
>                 ERR(handle, "Invalid avtab");
>                 return -1;
>         }
> @@ -845,7 +852,7 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
>                 goto bad;
>
>         if (p->policy_type == POLICY_KERN) {
> -               if (validate_avtab(handle, &p->te_avtab, flavors))
> +               if (validate_avtabs(handle, &p->te_avtab, flavors))
>                         goto bad;
>                 if (p->policyvers >= POLICYDB_VERSION_BOOL)
>                         if (validate_cond_list(handle, p->cond_list, flavors))
> --
> 2.33.0
>

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH 28/35] libsepol: validate ocontexts
  2021-10-11 16:25 ` [RFC PATCH 28/35] libsepol: validate ocontexts Christian Göttsche
@ 2021-10-14 14:10   ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-10-14 14:10 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Mon, Oct 11, 2021 at 12:41 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
>     ==91274==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f60b0afe8c6 bp 0x7ffd42edc990 sp 0x7ffd42edc148 T0)
>     ==91274==The signal is caused by a READ memory access.
>     ==91274==Hint: address points to the zero page.
>         #0 0x7f60b0afe8c6  string/../sysdeps/x86_64/multiarch/../strlen.S:120
>         #1 0x4bd128 in __interceptor_strlen (./out/binpolicy-fuzzer+0x4bd128)
>         #2 0x5eb387 in create_str_helper ./libsepol/src/kernel_to_common.c:69:10
>         #3 0x5eb11e in create_str ./libsepol/src/kernel_to_common.c:99:8
>         #4 0x56ad7b in context_to_str ./libsepol/src/kernel_to_conf.c:2408:9
>         #5 0x56a717 in write_sid_context_rules_to_conf ./libsepol/src/kernel_to_conf.c:2441:9
>         #6 0x55b26c in write_selinux_isid_rules_to_conf ./libsepol/src/kernel_to_conf.c:2476:9
>         #7 0x55b26c in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3206:8
>         #8 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
>         #9 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
>         #10 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
>         #11 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
>         #12 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
>         #13 0x7f60b0a887ec in __libc_start_main csu/../csu/libc-start.c:332:16
>         #14 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/src/policydb_validate.c | 37 ++++++++++++++++++++++++++++++++
>  1 file changed, 37 insertions(+)
>
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index 9134e541..5c06e6f4 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -677,6 +677,41 @@ static int validate_filename_trans_hashtab(sepol_handle_t *handle, hashtab_t fil
>         return 0;
>  }
>
> +static int validate_context(context_struct_t *con, validate_t flavors[], int mls)
> +{
> +       if (validate_value(con->user, &flavors[SYM_USERS]))
> +               return -1;
> +       if (validate_value(con->role, &flavors[SYM_ROLES]))
> +               return -1;
> +       if (validate_value(con->type, &flavors[SYM_TYPES]))
> +               return -1;
> +       if (mls && validate_mls_range(&con->range, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
> +               return -1;
> +
> +       return 0;
> +}
> +
> +static int validate_ocontexts(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
> +{
> +       ocontext_t *octx;
> +       unsigned int i;
> +
> +       for (i = 0; i < OCON_NUM; i++) {
> +               for (octx = p->ocontexts[i]; octx; octx = octx->next) {
> +                       if (validate_context(&octx->context[0], flavors, p->mls))
> +                               goto bad;
> +                       if ((i == OCON_FS || i == OCON_NETIF) && validate_context(&octx->context[1], flavors, p->mls))
> +                               goto bad;
> +               }
> +       }
> +
> +       return 0;
> +
> +bad:
> +       ERR(handle, "Invalid ocontext");
> +       return -1;
> +}
> +
>  /*
>   * Functions to validate a module policydb
>   */
> @@ -861,6 +896,8 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
>                         goto bad;
>                 if (validate_role_allows(handle, p->role_allow, flavors))
>                         goto bad;
> +               if (validate_ocontexts(handle, p, flavors))
> +                       goto bad;
>                 if (p->policyvers >= POLICYDB_VERSION_FILENAME_TRANS)
>                         if (validate_filename_trans_hashtab(handle, p->filename_trans, flavors))
>                                 goto bad;
> --
> 2.33.0
>

ocontexts are also in base modules.
Jim

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH 29/35] libsepol: validate genfs contexts
  2021-10-11 16:25 ` [RFC PATCH 29/35] libsepol: validate genfs contexts Christian Göttsche
@ 2021-10-14 14:10   ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-10-14 14:10 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Mon, Oct 11, 2021 at 12:41 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/src/policydb_validate.c | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
>
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index 5c06e6f4..63fd935c 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -712,6 +712,25 @@ bad:
>         return -1;
>  }
>
> +static int validate_genfs(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
> +{
> +       genfs_t *genfs;
> +       ocontext_t *octx;
> +
> +       for (genfs = p->genfs; genfs; genfs = genfs->next) {
> +               for (octx = genfs->head; octx; octx = octx->next) {
> +                       if (validate_context(&octx->context[0], flavors, p->mls))
> +                               goto bad;
> +               }
> +       }
> +
> +       return 0;
> +
> +bad:
> +       ERR(handle, "Invalid genfs");
> +       return -1;
> +}
> +
>  /*
>   * Functions to validate a module policydb
>   */
> @@ -898,6 +917,8 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
>                         goto bad;
>                 if (validate_ocontexts(handle, p, flavors))
>                         goto bad;
> +               if (validate_genfs(handle, p, flavors))
> +                       goto bad;
>                 if (p->policyvers >= POLICYDB_VERSION_FILENAME_TRANS)
>                         if (validate_filename_trans_hashtab(handle, p->filename_trans, flavors))
>                                 goto bad;
> --
> 2.33.0
>

Like ocontexts, genfs rules can be in base modules.
Jim

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH 25/35] libsepol: validate avtab types
  2021-10-11 16:25 ` [RFC PATCH 25/35] libsepol: validate avtab types Christian Göttsche
@ 2021-10-18 19:54   ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-10-18 19:54 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Mon, Oct 11, 2021 at 12:41 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Check for invalid avtab types.
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/src/policydb_validate.c | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
>
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index fa128794..89830ff3 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -441,6 +441,20 @@ static int validate_avtab_key(avtab_key_t *key, validate_t flavors[])
>                 goto bad;
>         if (validate_value(key->target_class, &flavors[SYM_CLASSES]))
>                 goto bad;
> +       switch (0xFFF & key->specified) {
> +       case AVTAB_ALLOWED:
> +       case AVTAB_AUDITALLOW:
> +       case AVTAB_AUDITDENY:
> +       case AVTAB_XPERMS_ALLOWED:
> +       case AVTAB_XPERMS_AUDITALLOW:
> +       case AVTAB_XPERMS_DONTAUDIT:
> +       case AVTAB_TRANSITION:
> +       case AVTAB_MEMBER:
> +       case AVTAB_CHANGE:
> +               break;
> +       default:
> +               goto bad;
> +       }
>
>         return 0;
>
> --
> 2.33.0
>

avrule_t also has a specified field that could be checked.

Thanks,
Jim

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH 14/35] libsepol: reject invalid fsuse types
  2021-10-11 16:25 ` [RFC PATCH 14/35] libsepol: reject invalid fsuse types Christian Göttsche
@ 2021-10-18 19:57   ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-10-18 19:57 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Mon, Oct 11, 2021 at 12:41 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Reject loading a policy with invalid fsuse declarations, except xattr,
> trans and task, so that all following code, e.g. the different output
> modes, do not need to handle unsupported ones.
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/src/policydb.c | 9 +++++++++
>  1 file changed, 9 insertions(+)
>
> diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
> index 70b503e1..980af059 100644
> --- a/libsepol/src/policydb.c
> +++ b/libsepol/src/policydb.c
> @@ -48,6 +48,7 @@
>  #include <sepol/policydb/expand.h>
>  #include <sepol/policydb/conditional.h>
>  #include <sepol/policydb/avrule_block.h>
> +#include <sepol/policydb/services.h>
>  #include <sepol/policydb/util.h>
>
>  #include "kernel_to_common.h"
> @@ -3099,6 +3100,14 @@ static int ocontext_read_selinux(const struct policydb_compat_info *info,
>                                 if (rc < 0)
>                                         return -1;
>                                 c->v.behavior = le32_to_cpu(buf[0]);
> +                               switch (c->v.behavior) {
> +                               case SECURITY_FS_USE_XATTR:
> +                               case SECURITY_FS_USE_TRANS:
> +                               case SECURITY_FS_USE_TASK:
> +                                       break;
> +                               default:
> +                                       return -1;
> +                               }
>                                 len = le32_to_cpu(buf[1]);
>                                 if (zero_or_saturated(len))
>                                         return -1;
> --
> 2.33.0
>

I think that I would prefer this to be in policydb_validate.c
somewhere. Eventually it would be nice to call validate_policydb() on
a policydb before writing it as well as after reading it.

Thanks,
Jim

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH 15/35] libsepol: reject invalid default targets
  2021-10-11 16:25 ` [RFC PATCH 15/35] libsepol: reject invalid default targets Christian Göttsche
@ 2021-10-18 19:58   ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-10-18 19:58 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Mon, Oct 11, 2021 at 12:41 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Reject loading a policy with invalid default targets so that all
> following code, e.g. the different output modes, do not need to handle
> unsupported ones.
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/src/policydb.c | 37 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 37 insertions(+)
>
> diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
> index 980af059..5e8b4a3f 100644
> --- a/libsepol/src/policydb.c
> +++ b/libsepol/src/policydb.c
> @@ -2313,8 +2313,37 @@ static int class_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
>                 if (rc < 0)
>                         goto bad;
>                 cladatum->default_user = le32_to_cpu(buf[0]);
> +               switch (cladatum->default_user) {
> +               case 0:
> +               case DEFAULT_SOURCE:
> +               case DEFAULT_TARGET:
> +                       break;
> +               default:
> +                       goto bad;
> +               }
>                 cladatum->default_role = le32_to_cpu(buf[1]);
> +               switch (cladatum->default_role) {
> +               case 0:
> +               case DEFAULT_SOURCE:
> +               case DEFAULT_TARGET:
> +                       break;
> +               default:
> +                       goto bad;
> +               }
>                 cladatum->default_range = le32_to_cpu(buf[2]);
> +               switch (cladatum->default_range) {
> +               case 0:
> +               case DEFAULT_SOURCE_LOW:
> +               case DEFAULT_SOURCE_HIGH:
> +               case DEFAULT_SOURCE_LOW_HIGH:
> +               case DEFAULT_TARGET_LOW:
> +               case DEFAULT_TARGET_HIGH:
> +               case DEFAULT_TARGET_LOW_HIGH:
> +               case DEFAULT_GLBLUB:
> +                       break;
> +               default:
> +                       goto bad;
> +               }
>         }
>
>         if ((p->policy_type == POLICY_KERN &&
> @@ -2325,6 +2354,14 @@ static int class_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
>                 if (rc < 0)
>                         goto bad;
>                 cladatum->default_type = le32_to_cpu(buf[0]);
> +               switch (cladatum->default_type) {
> +               case 0:
> +               case DEFAULT_SOURCE:
> +               case DEFAULT_TARGET:
> +                       break;
> +               default:
> +                       goto bad;
> +               }
>         }
>
>         if (hashtab_insert(h, key, cladatum))
> --
> 2.33.0
>

Same comment as for patch 14, I would like to see this in
policydb_validate.c and checked when validating the policydb.

Thanks,
Jim

^ permalink raw reply	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 00/36] libsepol: add fuzzer for reading binary policies
  2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
                   ` (35 preceding siblings ...)
  2021-10-13 14:07 ` [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies James Carter
@ 2021-11-05 15:45 ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 01/36] cifuzz: enable report-unreproducible-crashes Christian Göttsche
                     ` (41 more replies)
  36 siblings, 42 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Add a libfuzz[1] based fuzzer testing the reading and parsing of binary policy
files. This fuzzer will be run within the OSS-Fuzz service.

Handle and reject a variety of edge cases causing crashes or resource leaks.

The fifth patch ("libsepol/fuzz: limit element sizes for fuzzing") needs some
discussion: To avoid oom reports from the fuzzer, caused by huge memory
allocations, all identifiers are limited to a length of 2^16 for the fuzzer
build only.  Probably there should be a limit for the release build too.
Is there a specification for the binary policy format saying something about
the maximum length of identifiers?
After a quick look at the kernel sources (most interesting is str_read()) I
could not find any limits either.

[1]: https://llvm.org/docs/LibFuzzer.html

v2:
  - reorder patches
    1. oss-fuzz related
    2. libsepol parsing and other crashesand UB
    3. enhance policy validation
  - misc changes based on review by James Carter

Christian Göttsche (36):
  cifuzz: enable report-unreproducible-crashes
  cifuzz: use the default runtime of 600 seconds
  libsepol/fuzz: silence secilc-fuzzer
  libsepol: add libfuzz based fuzzer for reading binary policies
  libsepol/fuzz: limit element sizes for fuzzing
  libsepol: use logging framework in conditional.c
  libsepol: use logging framework in ebitmap.c
  libsepol: use mallocarray wrapper to avoid overflows
  libsepol: use reallocarray wrapper to avoid overflows
  libsepol: add checks for read sizes
  libsepol: enforce avtab item limit
  libsepol: clean memory on conditional insertion failure
  libsepol: reject abnormal huge sid ids
  libsepol: reject invalid filetrans source type
  libsepol: zero member before potential dereference
  libsepol: use size_t for indexes in strs helpers
  libsepol: do not underflow on short format arguments
  libsepol: do not crash on class gaps
  libsepol: do not crash on user gaps
  libsepol: use correct size for initial string list
  libsepol: do not create a string list with initial size zero
  libsepol: split validation of datum array gaps and entries
  libsepol: validate MLS levels
  libsepol: validate expanded user range and level
  libsepol: validate permission count of classes
  libsepol: resolve log message mismatch
  libsepol: validate avtab and avrule types
  libsepol: validate constraint expression operators and attributes
  libsepol: validate type of avtab type rules
  libsepol: validate ocontexts
  libsepol: validate genfs contexts
  libsepol: validate permissive types
  libsepol: validate policy properties
  libsepol: validate categories
  libsepol: validate fsuse types
  libsepol: validate class default targets

 .github/workflows/cifuzz.yml     |   3 +-
 libsepol/fuzz/binpolicy-fuzzer.c |  63 ++++
 libsepol/fuzz/policy.bin         | Bin 0 -> 1552 bytes
 libsepol/fuzz/secilc-fuzzer.c    |   5 +
 libsepol/src/Makefile            |   6 +
 libsepol/src/avtab.c             |   6 +
 libsepol/src/conditional.c       |  53 ++--
 libsepol/src/ebitmap.c           |  27 +-
 libsepol/src/expand.c            |   4 +-
 libsepol/src/hashtab.c           |   4 +-
 libsepol/src/kernel_to_cil.c     |  10 +
 libsepol/src/kernel_to_common.c  |  23 +-
 libsepol/src/kernel_to_common.h  |   4 +-
 libsepol/src/kernel_to_conf.c    |  13 +-
 libsepol/src/link.c              |   3 +-
 libsepol/src/module.c            |   4 +-
 libsepol/src/module_to_cil.c     |  13 +-
 libsepol/src/optimize.c          |  11 +-
 libsepol/src/policydb.c          |  27 +-
 libsepol/src/policydb_validate.c | 475 +++++++++++++++++++++++++++----
 libsepol/src/private.h           |  27 +-
 libsepol/src/services.c          |  12 +-
 libsepol/src/sidtab.c            |   3 +-
 libsepol/src/user_record.c       |   8 +-
 libsepol/src/users.c             |  12 +-
 libsepol/src/util.c              |  11 +-
 libsepol/src/write.c             |   2 +-
 scripts/oss-fuzz.sh              |  17 +-
 28 files changed, 684 insertions(+), 162 deletions(-)
 create mode 100644 libsepol/fuzz/binpolicy-fuzzer.c
 create mode 100644 libsepol/fuzz/policy.bin

-- 
2.33.1


^ permalink raw reply	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 01/36] cifuzz: enable report-unreproducible-crashes
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 02/36] cifuzz: use the default runtime of 600 seconds Christian Göttsche
                     ` (40 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Fail and report unreproducible fuzzing crashes and leaks. Such failures
are probably related to some global state not properly reset in the
fuzzer and can cause OSS-Fuzz to report flaky issues.

Suggested-by: Evgeny Vereshchagin
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 .github/workflows/cifuzz.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
index 5c2233a2..b28eb71a 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/cifuzz.yml
@@ -30,6 +30,7 @@ jobs:
           oss-fuzz-project-name: 'selinux'
           fuzz-seconds: 180
           dry-run: false
+          report-unreproducible-crashes: true
           sanitizer: ${{ matrix.sanitizer }}
       - name: Upload Crash
         uses: actions/upload-artifact@v1
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 02/36] cifuzz: use the default runtime of 600 seconds
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 01/36] cifuzz: enable report-unreproducible-crashes Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 03/36] libsepol/fuzz: silence secilc-fuzzer Christian Göttsche
                     ` (39 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

The default runtime for CIFuzz[1] is 600 seconds; use it.

Since GitHub pull-requests are not the main contribution workflow the
number of runs should be manageable.

[1]: https://google.github.io/oss-fuzz/getting-started/continuous-integration/

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 .github/workflows/cifuzz.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
index b28eb71a..92523db4 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/cifuzz.yml
@@ -28,7 +28,7 @@ jobs:
         uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
         with:
           oss-fuzz-project-name: 'selinux'
-          fuzz-seconds: 180
+          fuzz-seconds: 600
           dry-run: false
           report-unreproducible-crashes: true
           sanitizer: ${{ matrix.sanitizer }}
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 03/36] libsepol/fuzz: silence secilc-fuzzer
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 01/36] cifuzz: enable report-unreproducible-crashes Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 02/36] cifuzz: use the default runtime of 600 seconds Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 04/36] libsepol: add libfuzz based fuzzer for reading binary policies Christian Göttsche
                     ` (38 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Do not output CIL log messages while fuzzing, since their amount are
huge, e.g. for neverallow or typebounds violations.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/fuzz/secilc-fuzzer.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libsepol/fuzz/secilc-fuzzer.c b/libsepol/fuzz/secilc-fuzzer.c
index 255b3241..9a1a16de 100644
--- a/libsepol/fuzz/secilc-fuzzer.c
+++ b/libsepol/fuzz/secilc-fuzzer.c
@@ -8,6 +8,10 @@
 #include <sepol/cil/cil.h>
 #include <sepol/policydb.h>
 
+static void log_handler(__attribute__((unused)) int lvl, __attribute__((unused)) const char *msg) {
+	/* be quiet */
+}
+
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
 	enum cil_log_level log_level = CIL_ERR;
 	struct sepol_policy_file *pf = NULL;
@@ -24,6 +28,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
 	sepol_policydb_t *pdb = NULL;
 
 	cil_set_log_level(log_level);
+	cil_set_log_handler(log_handler);
 
 	cil_db_init(&db);
 	cil_set_disable_dontaudit(db, disable_dontaudit);
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 04/36] libsepol: add libfuzz based fuzzer for reading binary policies
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (2 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 03/36] libsepol/fuzz: silence secilc-fuzzer Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 05/36] libsepol/fuzz: limit element sizes for fuzzing Christian Göttsche
                     ` (37 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Introduce a libfuzz[1] based fuzzer testing the parsing of a binary
policy.

Build the fuzzer in the oss-fuzz script.

[1]: https://llvm.org/docs/LibFuzzer.html

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/fuzz/binpolicy-fuzzer.c |  63 +++++++++++++++++++++++++++++++
 libsepol/fuzz/policy.bin         | Bin 0 -> 1552 bytes
 scripts/oss-fuzz.sh              |  17 ++++++++-
 3 files changed, 78 insertions(+), 2 deletions(-)
 create mode 100644 libsepol/fuzz/binpolicy-fuzzer.c
 create mode 100644 libsepol/fuzz/policy.bin

diff --git a/libsepol/fuzz/binpolicy-fuzzer.c b/libsepol/fuzz/binpolicy-fuzzer.c
new file mode 100644
index 00000000..85c59645
--- /dev/null
+++ b/libsepol/fuzz/binpolicy-fuzzer.c
@@ -0,0 +1,63 @@
+#include <sepol/debug.h>
+#include <sepol/kernel_to_cil.h>
+#include <sepol/kernel_to_conf.h>
+#include <sepol/policydb/policydb.h>
+
+extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static int write_binary_policy(policydb_t *p, FILE *outfp)
+{
+	struct policy_file pf;
+
+	policy_file_init(&pf);
+	pf.type = PF_USE_STDIO;
+	pf.fp = outfp;
+	return policydb_write(p, &pf);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+	policydb_t policydb = {};
+	sidtab_t sidtab = {};
+	struct policy_file pf;
+	FILE *devnull = NULL;
+
+	sepol_debug(0);
+
+	policy_file_init(&pf);
+	pf.type = PF_USE_MEMORY;
+	pf.data = (char *) data;
+	pf.len = size;
+
+	if (policydb_init(&policydb))
+		goto exit;
+
+	if (policydb_read(&policydb, &pf, /*verbose=*/0))
+		goto exit;
+
+	if (policydb_load_isids(&policydb, &sidtab))
+		goto exit;
+
+	if (policydb.policy_type == POLICY_KERN)
+		(void) policydb_optimize(&policydb);
+
+	devnull = fopen("/dev/null", "w");
+	if (!devnull)
+		goto exit;
+
+	(void) write_binary_policy(&policydb, devnull);
+
+	(void) sepol_kernel_policydb_to_conf(devnull, &policydb);
+
+	(void) sepol_kernel_policydb_to_cil(devnull, &policydb);
+
+exit:
+	if (devnull != NULL)
+		fclose(devnull);
+
+	policydb_destroy(&policydb);
+	sepol_sidtab_destroy(&sidtab);
+
+	/* Non-zero return values are reserved for future use. */
+	return 0;
+}
diff --git a/libsepol/fuzz/policy.bin b/libsepol/fuzz/policy.bin
new file mode 100644
index 0000000000000000000000000000000000000000..6f977ef34479daa9bf2e848c502ecea8d96f7912
GIT binary patch
literal 1552
zcma)5OLBuS3?==4PtZ+{&?9)$U3WbIlYnX65X0D})6Db;y>M5p9{5ov4Nx%;$<mW$
z3H<r}@pX|T$<xE~(b(pFDfU7D-=#naD2m2Fg9jW(-^m~bGdGSNY)e53<fv2qdtGkQ
z!jzhhLpdx(PWIwvbIwVQy0qhU$VF|O4}e{}D%0NI#$~><!L6(}!BqAt@_s$c!a#sw
zC$j7XLx!Aos(%-zs7Bl3l+Sv4XN--GML2e*`6?Tq1A9jjY>40a)K#TcVgu}o@qItz
z*n@Vpe$`n>9k>)lLo|5!#vC+5dA)f~edbIZ(r^=r47z&T$5@O7acJXBjy1qIauI91
zZV#hm%^Wra%{;^@N(_Mfp@x57&=A0V{XH^N#4uZ2$+ZB!To<dR3|?FRA3At3Wr~g%
zz~016vi3X+@#ERQVqoAOx)TgDxswt<g<podAL6jTDGsjGTrHewj)RLe$3eey9G;aL
td_V~(^i6Tdr3M$kUC#Ae9okPlwF6@KhlL%sbur5q>K{?!0dQgn^$*KOS$hBg

literal 0
HcmV?d00001

diff --git a/scripts/oss-fuzz.sh b/scripts/oss-fuzz.sh
index 16cc3c0a..72d275e8 100755
--- a/scripts/oss-fuzz.sh
+++ b/scripts/oss-fuzz.sh
@@ -32,7 +32,7 @@ SANITIZER=${SANITIZER:-address}
 flags="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=$SANITIZER -fsanitize=fuzzer-no-link"
 
 export CC=${CC:-clang}
-export CFLAGS=${CFLAGS:-$flags}
+export CFLAGS="${CFLAGS:-$flags} -I$DESTDIR/usr/include -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
 
 export CXX=${CXX:-clang++}
 export CXXFLAGS=${CXXFLAGS:-$flags}
@@ -49,11 +49,24 @@ make -C libsepol clean
 # shellcheck disable=SC2016
 make -C libsepol V=1 LD_SONAME_FLAGS='-soname,$(LIBSO),--version-script=$(LIBMAP)' -j"$(nproc)" install
 
+## secilc fuzzer ##
+
 # CFLAGS, CXXFLAGS and LIB_FUZZING_ENGINE have to be split to be accepted by
 # the compiler/linker so they shouldn't be quoted
 # shellcheck disable=SC2086
-$CC $CFLAGS -I"$DESTDIR/usr/include" -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -c -o secilc-fuzzer.o libsepol/fuzz/secilc-fuzzer.c
+$CC $CFLAGS -c -o secilc-fuzzer.o libsepol/fuzz/secilc-fuzzer.c
 # shellcheck disable=SC2086
 $CXX $CXXFLAGS $LIB_FUZZING_ENGINE secilc-fuzzer.o "$DESTDIR/usr/lib/libsepol.a" -o "$OUT/secilc-fuzzer"
 
 zip -r "$OUT/secilc-fuzzer_seed_corpus.zip" secilc/test
+
+## binary policy fuzzer ##
+
+# CFLAGS, CXXFLAGS and LIB_FUZZING_ENGINE have to be split to be accepted by
+# the compiler/linker so they shouldn't be quoted
+# shellcheck disable=SC2086
+$CC $CFLAGS -c -o binpolicy-fuzzer.o libsepol/fuzz/binpolicy-fuzzer.c
+# shellcheck disable=SC2086
+$CXX $CXXFLAGS $LIB_FUZZING_ENGINE binpolicy-fuzzer.o "$DESTDIR/usr/lib/libsepol.a" -o "$OUT/binpolicy-fuzzer"
+
+zip -j "$OUT/binpolicy-fuzzer_seed_corpus.zip" libsepol/fuzz/policy.bin
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 05/36] libsepol/fuzz: limit element sizes for fuzzing
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (3 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 04/36] libsepol: add libfuzz based fuzzer for reading binary policies Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 06/36] libsepol: use logging framework in conditional.c Christian Göttsche
                     ` (36 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Limit the maximum length of read sizes, like string length of module
version and name or keys and number of symtab entries.  This avoids the
fuzzer to report oom events for huge allocations (it also improves the
number of executions per seconds of the fuzzer).

This change only affects the fuzzer build.

    ==15211== ERROR: libFuzzer: out-of-memory (malloc(3115956666))
       To change the out-of-memory limit use -rss_limit_mb=<N>

        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aa143 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa143)
        #7 0x5259cb in malloc (./out/binpolicy-fuzzer+0x5259cb)
        #8 0x59d307 in str_read ./libsepol/src/services.c:1746:8
        #9 0x585b97 in perm_read ./libsepol/src/policydb.c:2063:5
        #10 0x581f8a in common_read ./libsepol/src/policydb.c:2119:7
        #11 0x576681 in policydb_read ./libsepol/src/policydb.c:4417:8
        #12 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
        #13 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #14 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #15 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #16 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #17 0x7fe1ec88a7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #18 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

    ==12683== ERROR: libFuzzer: out-of-memory (malloc(2526451450))
       To change the out-of-memory limit use -rss_limit_mb=<N>

        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aa143 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa143)
        #7 0x5259cb in malloc (./out/binpolicy-fuzzer+0x5259cb)
        #8 0x575f8a in policydb_read ./libsepol/src/policydb.c:4356:18
        #9 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
        #10 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #11 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #12 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #13 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #14 0x7fa737b377ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #15 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/private.h | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/libsepol/src/private.h b/libsepol/src/private.h
index 71287282..6146f59f 100644
--- a/libsepol/src/private.h
+++ b/libsepol/src/private.h
@@ -44,7 +44,12 @@
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
 
-#define is_saturated(x) (x == (typeof(x))-1)
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+# define is_saturated(x) (x == (typeof(x))-1 || (x) > (1U << 16))
+#else
+# define is_saturated(x) (x == (typeof(x))-1)
+#endif
+
 #define zero_or_saturated(x) ((x == 0) || is_saturated(x))
 
 #define spaceship_cmp(a, b) (((a) > (b)) - ((a) < (b)))
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 06/36] libsepol: use logging framework in conditional.c
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (4 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 05/36] libsepol/fuzz: limit element sizes for fuzzing Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 07/36] libsepol: use logging framework in ebitmap.c Christian Göttsche
                     ` (35 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Use the internal logging framework instead of directly writing to
stdout as it might be undesired to do so within a library.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   replace INFO calls by WARN since they are reasons of failure
---
 libsepol/src/conditional.c | 30 +++++++++++-------------------
 1 file changed, 11 insertions(+), 19 deletions(-)

diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c
index 037dc7e2..1edac65d 100644
--- a/libsepol/src/conditional.c
+++ b/libsepol/src/conditional.c
@@ -25,6 +25,7 @@
 #include <sepol/policydb/conditional.h>
 
 #include "private.h"
+#include "debug.h"
 
 /* move all type rules to top of t/f lists to help kernel on evaluation */
 static void cond_optimize(cond_av_list_t ** l)
@@ -314,8 +315,7 @@ static int evaluate_cond_node(policydb_t * p, cond_node_t * node)
 	if (new_state != node->cur_state) {
 		node->cur_state = new_state;
 		if (new_state == -1)
-			printf
-			    ("expression result was undefined - disabling all rules.\n");
+			WARN(NULL, "expression result was undefined - disabling all rules.\n");
 		/* turn the rules on or off */
 		for (cur = node->true_list; cur != NULL; cur = cur->next) {
 			if (new_state <= 0) {
@@ -368,8 +368,7 @@ int cond_normalize_expr(policydb_t * p, cond_node_t * cn)
 		if (ne) {
 			ne->next = NULL;
 		} else {	/* ne should never be NULL */
-			printf
-			    ("Found expr with no bools and only a ! - this should never happen.\n");
+			ERR(NULL, "Found expr with no bools and only a ! - this should never happen.\n");
 			return -1;
 		}
 		/* swap the true and false lists */
@@ -421,8 +420,7 @@ int cond_normalize_expr(policydb_t * p, cond_node_t * cn)
 			}
 			k = cond_evaluate_expr(p, cn->expr);
 			if (k == -1) {
-				printf
-				    ("While testing expression, expression result "
+				ERR(NULL, "While testing expression, expression result "
 				     "was undefined - this should never happen.\n");
 				return -1;
 			}
@@ -635,8 +633,7 @@ static int cond_insertf(avtab_t * a
 	 */
 	if (k->specified & AVTAB_TYPE) {
 		if (avtab_search(&p->te_avtab, k)) {
-			printf
-			    ("security: type rule already exists outside of a conditional.");
+			WARN(NULL, "security: type rule already exists outside of a conditional.");
 			goto err;
 		}
 		/*
@@ -652,8 +649,7 @@ static int cond_insertf(avtab_t * a
 			if (node_ptr) {
 				if (avtab_search_node_next
 				    (node_ptr, k->specified)) {
-					printf
-					    ("security: too many conflicting type rules.");
+					ERR(NULL, "security: too many conflicting type rules.");
 					goto err;
 				}
 				found = 0;
@@ -664,15 +660,13 @@ static int cond_insertf(avtab_t * a
 					}
 				}
 				if (!found) {
-					printf
-					    ("security: conflicting type rules.\n");
+					ERR(NULL, "security: conflicting type rules.\n");
 					goto err;
 				}
 			}
 		} else {
 			if (avtab_search(&p->te_cond_avtab, k)) {
-				printf
-				    ("security: conflicting type rules when adding type rule for true.\n");
+				ERR(NULL, "security: conflicting type rules when adding type rule for true.\n");
 				goto err;
 			}
 		}
@@ -680,7 +674,7 @@ static int cond_insertf(avtab_t * a
 
 	node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d);
 	if (!node_ptr) {
-		printf("security: could not insert rule.");
+		ERR(NULL, "security: could not insert rule.");
 		goto err;
 	}
 	node_ptr->parse_context = (void *)1;
@@ -742,14 +736,12 @@ static int cond_read_av_list(policydb_t * p, void *fp,
 static int expr_isvalid(policydb_t * p, cond_expr_t * expr)
 {
 	if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) {
-		printf
-		    ("security: conditional expressions uses unknown operator.\n");
+		WARN(NULL, "security: conditional expressions uses unknown operator.\n");
 		return 0;
 	}
 
 	if (expr->bool > p->p_bools.nprim) {
-		printf
-		    ("security: conditional expressions uses unknown bool.\n");
+		WARN(NULL, "security: conditional expressions uses unknown bool.\n");
 		return 0;
 	}
 	return 1;
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 07/36] libsepol: use logging framework in ebitmap.c
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (5 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 06/36] libsepol: use logging framework in conditional.c Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 08/36] libsepol: use mallocarray wrapper to avoid overflows Christian Göttsche
                     ` (34 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Use the internal logging framework instead of directly writing to
stdout as it might be undesired to do so within a library.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/ebitmap.c | 27 ++++++++++-----------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/libsepol/src/ebitmap.c b/libsepol/src/ebitmap.c
index 1de3816a..fa728558 100644
--- a/libsepol/src/ebitmap.c
+++ b/libsepol/src/ebitmap.c
@@ -406,8 +406,7 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 	count = le32_to_cpu(buf[2]);
 
 	if (mapsize != MAPSIZE) {
-		printf
-		    ("security: ebitmap: map size %d does not match my size %zu (high bit was %d)\n",
+		ERR(NULL, "security: ebitmap: map size %d does not match my size %zu (high bit was %d)\n",
 		     mapsize, MAPSIZE, e->highbit);
 		goto bad;
 	}
@@ -416,8 +415,7 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 		goto ok;
 	}
 	if (e->highbit & (MAPSIZE - 1)) {
-		printf
-		    ("security: ebitmap: high bit (%d) is not a multiple of the map size (%zu)\n",
+		ERR(NULL, "security: ebitmap: high bit (%d) is not a multiple of the map size (%zu)\n",
 		     e->highbit, MAPSIZE);
 		goto bad;
 	}
@@ -429,12 +427,12 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 	for (i = 0; i < count; i++) {
 		rc = next_entry(buf, fp, sizeof(uint32_t));
 		if (rc < 0) {
-			printf("security: ebitmap: truncated map\n");
+			ERR(NULL, "security: ebitmap: truncated map\n");
 			goto bad;
 		}
 		n = (ebitmap_node_t *) malloc(sizeof(ebitmap_node_t));
 		if (!n) {
-			printf("security: ebitmap: out of memory\n");
+			ERR(NULL, "security: ebitmap: out of memory\n");
 			rc = -ENOMEM;
 			goto bad;
 		}
@@ -443,34 +441,30 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 		n->startbit = le32_to_cpu(buf[0]);
 
 		if (n->startbit & (MAPSIZE - 1)) {
-			printf
-			    ("security: ebitmap start bit (%d) is not a multiple of the map size (%zu)\n",
+			ERR(NULL, "security: ebitmap start bit (%d) is not a multiple of the map size (%zu)\n",
 			     n->startbit, MAPSIZE);
 			goto bad_free;
 		}
 		if (n->startbit > (e->highbit - MAPSIZE)) {
-			printf
-			    ("security: ebitmap start bit (%d) is beyond the end of the bitmap (%zu)\n",
+			ERR(NULL, "security: ebitmap start bit (%d) is beyond the end of the bitmap (%zu)\n",
 			     n->startbit, (e->highbit - MAPSIZE));
 			goto bad_free;
 		}
 		rc = next_entry(&map, fp, sizeof(uint64_t));
 		if (rc < 0) {
-			printf("security: ebitmap: truncated map\n");
+			ERR(NULL, "security: ebitmap: truncated map\n");
 			goto bad_free;
 		}
 		n->map = le64_to_cpu(map);
 
 		if (!n->map) {
-			printf
-			    ("security: ebitmap: null map in ebitmap (startbit %d)\n",
+			ERR(NULL, "security: ebitmap: null map in ebitmap (startbit %d)\n",
 			     n->startbit);
 			goto bad_free;
 		}
 		if (l) {
 			if (n->startbit <= l->startbit) {
-				printf
-				    ("security: ebitmap: start bit %d comes after start bit %d\n",
+				ERR(NULL, "security: ebitmap: start bit %d comes after start bit %d\n",
 				     n->startbit, l->startbit);
 				goto bad_free;
 			}
@@ -481,8 +475,7 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 		l = n;
 	}
 	if (count && l->startbit + MAPSIZE != e->highbit) {
-		printf
-		    ("security: ebitmap: high bit %u has not the expected value %zu\n",
+		ERR(NULL, "security: ebitmap: high bit %u has not the expected value %zu\n",
 		     e->highbit, l->startbit + MAPSIZE);
 		goto bad;
 	}
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 08/36] libsepol: use mallocarray wrapper to avoid overflows
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (6 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 07/36] libsepol: use logging framework in ebitmap.c Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 09/36] libsepol: use reallocarray " Christian Göttsche
                     ` (33 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Use a wrapper to guard `malloc(a * b)` type allocations, to detect
multiplication overflows, which result in too few memory being
allocated.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/conditional.c   | 2 +-
 libsepol/src/expand.c        | 4 ++--
 libsepol/src/hashtab.c       | 4 +++-
 libsepol/src/link.c          | 3 ++-
 libsepol/src/module.c        | 4 ++--
 libsepol/src/module_to_cil.c | 4 ++--
 libsepol/src/optimize.c      | 6 ++++--
 libsepol/src/policydb.c      | 6 +++---
 libsepol/src/private.h       | 9 +++++++++
 libsepol/src/services.c      | 6 +++---
 libsepol/src/sidtab.c        | 3 ++-
 libsepol/src/user_record.c   | 3 ++-
 libsepol/src/write.c         | 2 +-
 13 files changed, 36 insertions(+), 20 deletions(-)

diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c
index 1edac65d..cc3f4d82 100644
--- a/libsepol/src/conditional.c
+++ b/libsepol/src/conditional.c
@@ -522,7 +522,7 @@ int cond_init_bool_indexes(policydb_t * p)
 	if (p->bool_val_to_struct)
 		free(p->bool_val_to_struct);
 	p->bool_val_to_struct = (cond_bool_datum_t **)
-	    malloc(p->p_bools.nprim * sizeof(cond_bool_datum_t *));
+	    mallocarray(p->p_bools.nprim, sizeof(cond_bool_datum_t *));
 	if (!p->bool_val_to_struct)
 		return -1;
 	return 0;
diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
index a6a466f7..8a7259a0 100644
--- a/libsepol/src/expand.c
+++ b/libsepol/src/expand.c
@@ -3146,9 +3146,9 @@ int expand_module(sepol_handle_t * handle,
 		goto cleanup;
 
 	/* Build the type<->attribute maps and remove attributes. */
-	state.out->attr_type_map = malloc(state.out->p_types.nprim *
+	state.out->attr_type_map = mallocarray(state.out->p_types.nprim,
 					  sizeof(ebitmap_t));
-	state.out->type_attr_map = malloc(state.out->p_types.nprim *
+	state.out->type_attr_map = mallocarray(state.out->p_types.nprim,
 					  sizeof(ebitmap_t));
 	if (!state.out->attr_type_map || !state.out->type_attr_map) {
 		ERR(handle, "Out of memory!");
diff --git a/libsepol/src/hashtab.c b/libsepol/src/hashtab.c
index 21143b76..2eb35212 100644
--- a/libsepol/src/hashtab.c
+++ b/libsepol/src/hashtab.c
@@ -32,6 +32,8 @@
 #include <string.h>
 #include <sepol/policydb/hashtab.h>
 
+#include "private.h"
+
 hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h,
 						     const_hashtab_key_t key),
 			 int (*keycmp) (hashtab_t h,
@@ -52,7 +54,7 @@ hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h,
 	p->nel = 0;
 	p->hash_value = hash_value;
 	p->keycmp = keycmp;
-	p->htable = (hashtab_ptr_t *) malloc(sizeof(hashtab_ptr_t) * size);
+	p->htable = (hashtab_ptr_t *) mallocarray(size, sizeof(hashtab_ptr_t));
 	if (p->htable == NULL) {
 		free(p);
 		return NULL;
diff --git a/libsepol/src/link.c b/libsepol/src/link.c
index 7512a4d9..bd986b7b 100644
--- a/libsepol/src/link.c
+++ b/libsepol/src/link.c
@@ -34,6 +34,7 @@
 #include <assert.h>
 
 #include "debug.h"
+#include "private.h"
 
 #undef min
 #define min(a,b) (((a) < (b)) ? (a) : (b))
@@ -1679,7 +1680,7 @@ static int copy_scope_index(scope_index_t * src, scope_index_t * dest,
 	}
 
 	/* next copy the enabled permissions data  */
-	if ((dest->class_perms_map = malloc(largest_mapped_class_value *
+	if ((dest->class_perms_map = mallocarray(largest_mapped_class_value,
 					    sizeof(*dest->class_perms_map))) ==
 	    NULL) {
 		goto cleanup;
diff --git a/libsepol/src/module.c b/libsepol/src/module.c
index 02a5de2c..4a51f25c 100644
--- a/libsepol/src/module.c
+++ b/libsepol/src/module.c
@@ -406,14 +406,14 @@ static int module_package_read_offsets(sepol_module_package_t * mod,
 		goto err;
 	}
 
-	off = (size_t *) malloc((nsec + 1) * sizeof(size_t));
+	off = (size_t *) mallocarray(nsec + 1, sizeof(size_t));
 	if (!off) {
 		ERR(file->handle, "out of memory");
 		goto err;
 	}
 
 	free(buf);
-	buf = malloc(sizeof(uint32_t) * nsec);
+	buf = mallocarray(nsec, sizeof(uint32_t));
 	if (!buf) {
 		ERR(file->handle, "out of memory");
 		goto err;
diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c
index 16e4004e..ad0880bd 100644
--- a/libsepol/src/module_to_cil.c
+++ b/libsepol/src/module_to_cil.c
@@ -430,7 +430,7 @@ static int stack_init(struct stack **stack)
 		goto exit;
 	}
 
-	s->stack = malloc(sizeof(*s->stack) * STACK_SIZE);
+	s->stack = mallocarray(STACK_SIZE, sizeof(*s->stack));
 	if (s->stack == NULL) {
 		goto exit;
 	}
@@ -1008,7 +1008,7 @@ static int ebitmap_to_names(struct ebitmap *map, char **vals_to_names, char ***n
 		goto exit;
 	}
 
-	name_arr = malloc(sizeof(*name_arr) * num);
+	name_arr = mallocarray(num, sizeof(*name_arr));
 	if (name_arr == NULL) {
 		log_err("Out of memory");
 		rc = -1;
diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c
index 6826155c..f8298fb7 100644
--- a/libsepol/src/optimize.c
+++ b/libsepol/src/optimize.c
@@ -31,6 +31,8 @@
 #include <sepol/policydb/policydb.h>
 #include <sepol/policydb/conditional.h>
 
+#include "private.h"
+
 #define TYPE_VEC_INIT_SIZE 16
 
 struct type_vec {
@@ -42,7 +44,7 @@ static int type_vec_init(struct type_vec *v)
 {
 	v->capacity = TYPE_VEC_INIT_SIZE;
 	v->count = 0;
-	v->types = malloc(v->capacity * sizeof(*v->types));
+	v->types = mallocarray(v->capacity, sizeof(*v->types));
 	if (!v->types)
 		return -1;
 	return 0;
@@ -93,7 +95,7 @@ static struct type_vec *build_type_map(const policydb_t *p)
 {
 	unsigned int i, k;
 	ebitmap_node_t *n;
-	struct type_vec *map = malloc(p->p_types.nprim * sizeof(*map));
+	struct type_vec *map = mallocarray(p->p_types.nprim, sizeof(*map));
 	if (!map)
 		return NULL;
 
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 587ba64a..dcea1807 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -4111,7 +4111,7 @@ static int scope_read(policydb_t * p, int symnum, struct policy_file *fp)
 		goto cleanup;
 	}
 	if ((scope->decl_ids =
-	     malloc(scope->decl_ids_len * sizeof(uint32_t))) == NULL) {
+	     mallocarray(scope->decl_ids_len, sizeof(uint32_t))) == NULL) {
 		goto cleanup;
 	}
 	rc = next_entry(scope->decl_ids, fp, sizeof(uint32_t) * scope->decl_ids_len);
@@ -4500,8 +4500,8 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose)
 	}
 
 	if (policy_type == POLICY_KERN) {
-		p->type_attr_map = malloc(p->p_types.nprim * sizeof(ebitmap_t));
-		p->attr_type_map = malloc(p->p_types.nprim * sizeof(ebitmap_t));
+		p->type_attr_map = mallocarray(p->p_types.nprim, sizeof(ebitmap_t));
+		p->attr_type_map = mallocarray(p->p_types.nprim, sizeof(ebitmap_t));
 		if (!p->type_attr_map || !p->attr_type_map)
 			goto bad;
 		for (i = 0; i < p->p_types.nprim; i++) {
diff --git a/libsepol/src/private.h b/libsepol/src/private.h
index 6146f59f..d3d65a57 100644
--- a/libsepol/src/private.h
+++ b/libsepol/src/private.h
@@ -83,3 +83,12 @@ extern int next_entry(void *buf, struct policy_file *fp, size_t bytes);
 extern size_t put_entry(const void *ptr, size_t size, size_t n,
 		        struct policy_file *fp);
 extern int str_read(char **strp, struct policy_file *fp, size_t len);
+
+static inline void* mallocarray(size_t nmemb, size_t size) {
+	if (size && nmemb > (size_t)-1 / size) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	return malloc(nmemb * size);
+}
diff --git a/libsepol/src/services.c b/libsepol/src/services.c
index 3407058f..edcdde21 100644
--- a/libsepol/src/services.c
+++ b/libsepol/src/services.c
@@ -712,7 +712,7 @@ mls_ops:
 	 * Generate the same number of answer buffer entries as expression
 	 * buffers (as there will never be more).
 	 */
-	answer_list = malloc(expr_count * sizeof(*answer_list));
+	answer_list = mallocarray(expr_count, sizeof(*answer_list));
 	if (!answer_list) {
 		ERR(NULL, "failed to allocate answer stack");
 		rc = -ENOMEM;
@@ -2163,7 +2163,7 @@ int sepol_get_user_sids(sepol_security_id_t fromsid,
 	}
 	usercon.user = user->s.value;
 
-	mysids = malloc(maxnel * sizeof(sepol_security_id_t));
+	mysids = mallocarray(maxnel, sizeof(sepol_security_id_t));
 	if (!mysids) {
 		rc = -ENOMEM;
 		goto out;
@@ -2199,7 +2199,7 @@ int sepol_get_user_sids(sepol_security_id_t fromsid,
 			} else {
 				maxnel += SIDS_NEL;
 				mysids2 =
-				    malloc(maxnel *
+				    mallocarray(maxnel,
 					   sizeof(sepol_security_id_t));
 
 				if (!mysids2) {
diff --git a/libsepol/src/sidtab.c b/libsepol/src/sidtab.c
index 255e0725..adeae6eb 100644
--- a/libsepol/src/sidtab.c
+++ b/libsepol/src/sidtab.c
@@ -15,6 +15,7 @@
 #include <sepol/policydb/sidtab.h>
 
 #include "flask.h"
+#include "private.h"
 
 #define SIDTAB_HASH(sid) \
 (sid & SIDTAB_HASH_MASK)
@@ -27,7 +28,7 @@ int sepol_sidtab_init(sidtab_t * s)
 {
 	int i;
 
-	s->htable = malloc(sizeof(sidtab_ptr_t) * SIDTAB_SIZE);
+	s->htable = mallocarray(SIDTAB_SIZE, sizeof(sidtab_ptr_t));
 	if (!s->htable)
 		return -ENOMEM;
 	for (i = 0; i < SIDTAB_SIZE; i++)
diff --git a/libsepol/src/user_record.c b/libsepol/src/user_record.c
index ac520060..c1356a6b 100644
--- a/libsepol/src/user_record.c
+++ b/libsepol/src/user_record.c
@@ -4,6 +4,7 @@
 
 #include "user_internal.h"
 #include "debug.h"
+#include "private.h"
 
 struct sepol_user {
 	/* This user's name */
@@ -265,7 +266,7 @@ int sepol_user_get_roles(sepol_handle_t * handle,
 
 	unsigned int i;
 	const char **tmp_roles =
-	    (const char **)malloc(sizeof(char *) * user->num_roles);
+	    (const char **)mallocarray(user->num_roles, sizeof(char *));
 	if (!tmp_roles)
 		goto omem;
 
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index 3bd034d6..9df5b0bd 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -2117,7 +2117,7 @@ static int scope_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
 		 * buffer.  this would have been easier with C99's
 		 * dynamic arrays... */
 		rc = POLICYDB_ERROR;
-		dyn_buf = malloc(items * sizeof(*dyn_buf));
+		dyn_buf = mallocarray(items, sizeof(*dyn_buf));
 		if (!dyn_buf)
 			goto err;
 		buf = dyn_buf;
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 09/36] libsepol: use reallocarray wrapper to avoid overflows
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (7 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 08/36] libsepol: use mallocarray wrapper to avoid overflows Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 10/36] libsepol: add checks for read sizes Christian Göttsche
                     ` (32 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Use a wrapper to guard `realloc(p, a * b)` type allocations, to detect
multiplication overflows, which result in too few memory being
allocated.

Use a custom implementation if the used C library does not offer one.

Also use temporary variables for realloc(3) results in add_i_to_a() and
fp_to_buffer().

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/Makefile           |  6 ++++++
 libsepol/src/kernel_to_common.c |  4 ++--
 libsepol/src/module_to_cil.c    |  9 +++++----
 libsepol/src/optimize.c         |  5 +++--
 libsepol/src/private.h          | 11 +++++++++++
 libsepol/src/services.c         |  6 +++---
 libsepol/src/user_record.c      |  5 +++--
 libsepol/src/users.c            | 12 ++++++------
 libsepol/src/util.c             | 11 +++++++----
 9 files changed, 46 insertions(+), 23 deletions(-)

diff --git a/libsepol/src/Makefile b/libsepol/src/Makefile
index dc8b1773..13410c67 100644
--- a/libsepol/src/Makefile
+++ b/libsepol/src/Makefile
@@ -29,6 +29,12 @@ LOBJS += $(sort $(patsubst %.c,%.lo,$(sort $(wildcard $(CILDIR)/src/*.c)) $(CIL_
 override CFLAGS += -I$(CILDIR)/include
 endif
 
+# check for reallocarray(3) availability
+H := \#
+ifeq (yes,$(shell printf '${H}define _GNU_SOURCE\n${H}include <stdlib.h>\nint main(void){void*p=reallocarray(NULL, 1, sizeof(char));return 0;}' | $(CC) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
+override CFLAGS += -DHAVE_REALLOCARRAY
+endif
+
 LD_SONAME_FLAGS=-soname,$(LIBSO),--version-script=$(LIBMAP),-z,defs
 
 LN=ln
diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c
index a7453d3c..51df8c25 100644
--- a/libsepol/src/kernel_to_common.c
+++ b/libsepol/src/kernel_to_common.c
@@ -161,7 +161,7 @@ int strs_add(struct strs *strs, char *s)
 		char **new;
 		unsigned i = strs->size;
 		strs->size *= 2;
-		new = realloc(strs->list, sizeof(char *)*strs->size);
+		new = reallocarray(strs->list, strs->size, sizeof(char *));
 		if (!new) {
 			sepol_log_err("Out of memory");
 			return -1;
@@ -220,7 +220,7 @@ int strs_add_at_index(struct strs *strs, char *s, unsigned index)
 		while (index >= strs->size) {
 			strs->size *= 2;
 		}
-		new = realloc(strs->list, sizeof(char *)*strs->size);
+		new = reallocarray(strs->list, strs->size, sizeof(char *));
 		if (!new) {
 			sepol_log_err("Out of memory");
 			return -1;
diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c
index ad0880bd..84e49c5b 100644
--- a/libsepol/src/module_to_cil.c
+++ b/libsepol/src/module_to_cil.c
@@ -453,7 +453,7 @@ static int stack_push(struct stack *stack, void *ptr)
 	void *new_stack;
 
 	if (stack->pos + 1 == stack->size) {
-		new_stack = realloc(stack->stack, sizeof(*stack->stack) * (stack->size * 2));
+		new_stack = reallocarray(stack->stack, stack->size * 2, sizeof(*stack->stack));
 		if (new_stack == NULL) {
 			goto exit;
 		}
@@ -4123,7 +4123,7 @@ exit:
 static int fp_to_buffer(FILE *fp, char **data, size_t *data_len)
 {
 	int rc = -1;
-	char *d = NULL;
+	char *d = NULL, *d_tmp;
 	size_t d_len = 0;
 	size_t read_len = 0;
 	size_t max_len = 1 << 17; // start at 128KB, this is enough to hold about half of all the existing pp files
@@ -4139,12 +4139,13 @@ static int fp_to_buffer(FILE *fp, char **data, size_t *data_len)
 		d_len += read_len;
 		if (d_len == max_len) {
 			max_len *= 2;
-			d = realloc(d, max_len);
-			if (d == NULL) {
+			d_tmp = realloc(d, max_len);
+			if (d_tmp == NULL) {
 				log_err("Out of memory");
 				rc = -1;
 				goto exit;
 			}
+			d = d_tmp;
 		}
 	}
 
diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c
index f8298fb7..8a048702 100644
--- a/libsepol/src/optimize.c
+++ b/libsepol/src/optimize.c
@@ -59,8 +59,9 @@ static int type_vec_append(struct type_vec *v, uint32_t type)
 {
 	if (v->capacity == v->count) {
 		unsigned int new_capacity = v->capacity * 2;
-		uint32_t *new_types = realloc(v->types,
-					      new_capacity * sizeof(*v->types));
+		uint32_t *new_types = reallocarray(v->types,
+						   new_capacity,
+						   sizeof(*v->types));
 		if (!new_types)
 			return -1;
 
diff --git a/libsepol/src/private.h b/libsepol/src/private.h
index d3d65a57..a8cc1472 100644
--- a/libsepol/src/private.h
+++ b/libsepol/src/private.h
@@ -92,3 +92,14 @@ static inline void* mallocarray(size_t nmemb, size_t size) {
 
 	return malloc(nmemb * size);
 }
+
+#ifndef HAVE_REALLOCARRAY
+static inline void* reallocarray(void *ptr, size_t nmemb, size_t size) {
+	if (size && nmemb > (size_t)-1 / size) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	return realloc(ptr, nmemb * size);
+}
+#endif
diff --git a/libsepol/src/services.c b/libsepol/src/services.c
index edcdde21..0f36ac53 100644
--- a/libsepol/src/services.c
+++ b/libsepol/src/services.c
@@ -94,7 +94,7 @@ static void push(char *expr_ptr)
 		else
 			new_stack_len = stack_len * 2;
 
-		new_stack = realloc(stack, new_stack_len * sizeof(*stack));
+		new_stack = reallocarray(stack, new_stack_len, sizeof(*stack));
 		if (!new_stack) {
 			ERR(NULL, "unable to allocate stack space");
 			return;
@@ -449,8 +449,8 @@ static int constraint_expr_eval_reason(context_struct_t *scontext,
 			else
 				new_expr_list_len = expr_list_len * 2;
 
-			new_expr_list = realloc(expr_list,
-					new_expr_list_len * sizeof(*expr_list));
+			new_expr_list = reallocarray(expr_list,
+					new_expr_list_len, sizeof(*expr_list));
 			if (!new_expr_list) {
 				ERR(NULL, "failed to allocate expr buffer stack");
 				rc = -ENOMEM;
diff --git a/libsepol/src/user_record.c b/libsepol/src/user_record.c
index c1356a6b..404fa3a8 100644
--- a/libsepol/src/user_record.c
+++ b/libsepol/src/user_record.c
@@ -183,8 +183,9 @@ int sepol_user_add_role(sepol_handle_t * handle,
 	if (!role_cp)
 		goto omem;
 
-	roles_realloc = realloc(user->roles,
-				sizeof(char *) * (user->num_roles + 1));
+	roles_realloc = reallocarray(user->roles,
+				     user->num_roles + 1,
+				     sizeof(char *));
 	if (!roles_realloc)
 		goto omem;
 
diff --git a/libsepol/src/users.c b/libsepol/src/users.c
index b895b7f5..a7406214 100644
--- a/libsepol/src/users.c
+++ b/libsepol/src/users.c
@@ -226,17 +226,17 @@ int sepol_user_modify(sepol_handle_t * handle,
 		void *tmp_ptr;
 
 		/* Ensure reverse lookup array has enough space */
-		tmp_ptr = realloc(policydb->user_val_to_struct,
-				  (policydb->p_users.nprim +
-				   1) * sizeof(user_datum_t *));
+		tmp_ptr = reallocarray(policydb->user_val_to_struct,
+				  policydb->p_users.nprim + 1,
+				  sizeof(user_datum_t *));
 		if (!tmp_ptr)
 			goto omem;
 		policydb->user_val_to_struct = tmp_ptr;
 		policydb->user_val_to_struct[policydb->p_users.nprim] = NULL;
 
-		tmp_ptr = realloc(policydb->sym_val_to_name[SYM_USERS],
-				  (policydb->p_users.nprim +
-				   1) * sizeof(char *));
+		tmp_ptr = reallocarray(policydb->sym_val_to_name[SYM_USERS],
+				  policydb->p_users.nprim + 1,
+				  sizeof(char *));
 		if (!tmp_ptr)
 			goto omem;
 		policydb->sym_val_to_name[SYM_USERS] = tmp_ptr;
diff --git a/libsepol/src/util.c b/libsepol/src/util.c
index 902c63c5..b7230564 100644
--- a/libsepol/src/util.c
+++ b/libsepol/src/util.c
@@ -40,6 +40,8 @@ struct val_to_name {
  * 0).  Return 0 on success, -1 on out of memory. */
 int add_i_to_a(uint32_t i, uint32_t * cnt, uint32_t ** a)
 {
+	uint32_t *new;
+
 	if (cnt == NULL || a == NULL)
 		return -1;
 
@@ -48,17 +50,18 @@ int add_i_to_a(uint32_t i, uint32_t * cnt, uint32_t ** a)
 	 * than be smart about it, for now we realloc() the array each
 	 * time a new uint32_t is added! */
 	if (*a != NULL)
-		*a = (uint32_t *) realloc(*a, (*cnt + 1) * sizeof(uint32_t));
+		new = (uint32_t *) reallocarray(*a, *cnt + 1, sizeof(uint32_t));
 	else {			/* empty list */
 
 		*cnt = 0;
-		*a = (uint32_t *) malloc(sizeof(uint32_t));
+		new = (uint32_t *) malloc(sizeof(uint32_t));
 	}
-	if (*a == NULL) {
+	if (new == NULL) {
 		return -1;
 	}
-	(*a)[*cnt] = i;
+	new[*cnt] = i;
 	(*cnt)++;
+	*a = new;
 	return 0;
 }
 
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 10/36] libsepol: add checks for read sizes
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (8 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 09/36] libsepol: use reallocarray " Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-09 18:46     ` James Carter
  2021-11-05 15:45   ` [RFC PATCH v2 11/36] libsepol: enforce avtab item limit Christian Göttsche
                     ` (31 subsequent siblings)
  41 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Add checks for invalid read sizes from a binary policy to guard
allocations.

The common and class permission counts needs to be limited more strict
otherwise a too high count of common or class permissions can lead to
permission values with a too high value, which can lead to overflows
in shift operations.

In the fuzzer build the value will also be bounded to avoid oom reports.

    ==29857== ERROR: libFuzzer: out-of-memory (malloc(17179868160))
       To change the out-of-memory limit use -rss_limit_mb=<N>

        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aa143 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa143)
        #7 0x5259cb in malloc (./out/binpolicy-fuzzer+0x5259cb)
        #8 0x580b5d in mallocarray ./libsepol/src/./private.h:93:9
        #9 0x57c2ed in scope_read ./libsepol/src/policydb.c:4120:7
        #10 0x576b0d in policydb_read ./libsepol/src/policydb.c:4462:9
        #11 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
        #12 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #13 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #14 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #15 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #16 0x7ffad6e107ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #17 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

    ==19462== ERROR: libFuzzer: out-of-memory (malloc(18253611008))
       To change the out-of-memory limit use -rss_limit_mb=<N>

        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aa999 in __asan::asan_calloc(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa999)
        #7 0x525b63 in __interceptor_calloc (./out/binpolicy-fuzzer+0x525b63)
        #8 0x570938 in policydb_index_others ./libsepol/src/policydb.c:1245:6
        #9 0x5771f3 in policydb_read ./src/policydb.c:4481:6
        #10 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
        #11 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #12 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #13 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #14 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #15 0x7f4d933157ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #16 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index dcea1807..1408405d 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -2103,6 +2103,8 @@ static int common_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
 	if (symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE))
 		goto bad;
 	comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+	if (comdatum->permissions.nprim > 32)
+		goto bad;
 	nel = le32_to_cpu(buf[3]);
 
 	key = malloc(len + 1);
@@ -2251,6 +2253,8 @@ static int class_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
 	if (symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE))
 		goto bad;
 	cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+	if (cladatum->permissions.nprim > 32)
+		goto bad;
 	nel = le32_to_cpu(buf[4]);
 
 	ncons = le32_to_cpu(buf[5]);
@@ -3980,6 +3984,8 @@ static int avrule_decl_read(policydb_t * p, avrule_decl_t * decl,
 		if (rc < 0) 
 			return -1;
 		nprim = le32_to_cpu(buf[0]);
+		if (is_saturated(nprim))
+			return -1;
 		nel = le32_to_cpu(buf[1]);
 		for (j = 0; j < nel; j++) {
 			if (read_f[i] (p, decl->symtab[i].table, fp)) {
@@ -4106,7 +4112,7 @@ static int scope_read(policydb_t * p, int symnum, struct policy_file *fp)
 		goto cleanup;
 	scope->scope = le32_to_cpu(buf[0]);
 	scope->decl_ids_len = le32_to_cpu(buf[1]);
-	if (scope->decl_ids_len == 0) {
+	if (zero_or_saturated(scope->decl_ids_len)) {
 		ERR(fp->handle, "invalid scope with no declaration");
 		goto cleanup;
 	}
@@ -4396,6 +4402,8 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose)
 		if (rc < 0)
 			goto bad;
 		nprim = le32_to_cpu(buf[0]);
+		if (is_saturated(nprim))
+			goto bad;
 		nel = le32_to_cpu(buf[1]);
 		if (nel && !nprim) {
 			ERR(fp->handle, "unexpected items in symbol table with no symbol");
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 11/36] libsepol: enforce avtab item limit
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (9 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 10/36] libsepol: add checks for read sizes Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 12/36] libsepol: clean memory on conditional insertion failure Christian Göttsche
                     ` (30 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Check the current item count does not exceed the maximum allowed to
avoid stack overflows.

    ==33660==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fa64b8fc070 at pc 0x0000005acba0 bp 0x7ffc1f0b2870 sp 0x7ffc1f0b2868
    READ of size 4 at 0x7fa64b8fc070 thread T0
        #0 0x5acb9f in avtab_read_item ./libsepol/src/avtab.c:507:18
        #1 0x5acec4 in avtab_read ./libsepol/src/avtab.c:611:8
        #2 0x576ae3 in policydb_read ./libsepol/src/policydb.c:4433:7
        #3 0x55a1fe in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:24:6
        #4 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #5 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #6 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #7 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #8 0x7fa64cc867ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #9 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

    Address 0x7fa64b8fc070 is located in stack of thread T0 at offset 112 in frame
        #0 0x5aabdf in avtab_read_item ./libsepol/src/avtab.c:437

      This frame has 6 object(s):
        [32, 33) 'buf8' (line 438)
        [48, 56) 'buf16' (line 439)
        [80, 112) 'buf32' (line 440) <== Memory access at offset 112 overflows this variable
        [144, 152) 'key' (line 441)
        [176, 192) 'datum' (line 442)
        [208, 244) 'xperms' (line 443)
    HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
          (longjmp and C++ exceptions *are* supported)
    SUMMARY: AddressSanitizer: stack-buffer-overflow ./libsepol/src/avtab.c:507:18 in avtab_read_item
    Shadow bytes around the buggy address:
      0x0ff5497177b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff5497177c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff5497177d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff5497177e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff5497177f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    =>0x0ff549717800: f1 f1 f1 f1 01 f2 00 f2 f2 f2 00 00 00 00[f2]f2
      0x0ff549717810: f2 f2 00 f2 f2 f2 00 00 f2 f2 00 00 00 00 04 f3
      0x0ff549717820: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff549717830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff549717840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff549717850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    Shadow byte legend (one shadow byte represents 8 application bytes):
      Addressable:           00
      Partially addressable: 01 02 03 04 05 06 07
      Heap left redzone:       fa
      Freed heap region:       fd
      Stack left redzone:      f1
      Stack mid redzone:       f2
      Stack right redzone:     f3
      Stack after return:      f5
      Stack use after scope:   f8
      Global redzone:          f9
      Global init order:       f6
      Poisoned by user:        f7
      Container overflow:      fc
      Array cookie:            ac
      Intra object redzone:    bb
      ASan internal:           fe
      Left alloca redzone:     ca
      Right alloca redzone:    cb
    ==33660==ABORTING

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/avtab.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libsepol/src/avtab.c b/libsepol/src/avtab.c
index 46e1e75d..64aab683 100644
--- a/libsepol/src/avtab.c
+++ b/libsepol/src/avtab.c
@@ -503,6 +503,12 @@ int avtab_read_item(struct policy_file *fp, uint32_t vers, avtab_t * a,
 
 		for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
 			if (val & spec_order[i]) {
+				if (items > items2) {
+					ERR(fp->handle,
+					    "entry has too many items (%d/%d)",
+					    items, items2);
+					return -1;
+				}
 				key.specified = spec_order[i] | enabled;
 				datum.data = le32_to_cpu(buf32[items++]);
 				rc = insertf(a, &key, &datum, p);
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 12/36] libsepol: clean memory on conditional insertion failure
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (10 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 11/36] libsepol: enforce avtab item limit Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 13/36] libsepol: reject abnormal huge sid ids Christian Göttsche
                     ` (29 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Free the local access vector list on failure as it does not get moved
into the policy structure.
Drop the now redundant, but non-exhaustive, resource cleanup in
cond_insertf().

    Direct leak of 16 byte(s) in 1 object(s) allocated from:
        #0 0x52596d in malloc (./out/binpolicy-fuzzer+0x52596d)
        #1 0x5b30d2 in cond_insertf ./libsepol/src/conditional.c:682:9
        #2 0x5ac218 in avtab_read_item ./libsepol/src/avtab.c:583:10
        #3 0x5b21f4 in cond_read_av_list ./libsepol/src/conditional.c:725:8
        #4 0x5b21f4 in cond_read_node ./libsepol/src/conditional.c:798:7
        #5 0x5b21f4 in cond_read_list ./libsepol/src/conditional.c:847:7
        #6 0x576b6e in policydb_read ./libsepol/src/policydb.c:4436:8
        #7 0x55a1fe in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:24:6
        #8 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #9 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #10 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #11 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #12 0x7f47abeb87ec in __libc_start_main csu/../csu/libc-start.c:332:16

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   drop redundant cleanup in cond_insertf()
---
 libsepol/src/conditional.c | 21 +++++++++------------
 1 file changed, 9 insertions(+), 12 deletions(-)

diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c
index cc3f4d82..a3125fdd 100644
--- a/libsepol/src/conditional.c
+++ b/libsepol/src/conditional.c
@@ -634,7 +634,7 @@ static int cond_insertf(avtab_t * a
 	if (k->specified & AVTAB_TYPE) {
 		if (avtab_search(&p->te_avtab, k)) {
 			WARN(NULL, "security: type rule already exists outside of a conditional.");
-			goto err;
+			return -1;
 		}
 		/*
 		 * If we are reading the false list other will be a pointer to
@@ -650,7 +650,7 @@ static int cond_insertf(avtab_t * a
 				if (avtab_search_node_next
 				    (node_ptr, k->specified)) {
 					ERR(NULL, "security: too many conflicting type rules.");
-					goto err;
+					return -1;
 				}
 				found = 0;
 				for (cur = other; cur != NULL; cur = cur->next) {
@@ -661,13 +661,13 @@ static int cond_insertf(avtab_t * a
 				}
 				if (!found) {
 					ERR(NULL, "security: conflicting type rules.\n");
-					goto err;
+					return -1;
 				}
 			}
 		} else {
 			if (avtab_search(&p->te_cond_avtab, k)) {
 				ERR(NULL, "security: conflicting type rules when adding type rule for true.\n");
-				goto err;
+				return -1;
 			}
 		}
 	}
@@ -675,13 +675,13 @@ static int cond_insertf(avtab_t * a
 	node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d);
 	if (!node_ptr) {
 		ERR(NULL, "security: could not insert rule.");
-		goto err;
+		return -1;
 	}
 	node_ptr->parse_context = (void *)1;
 
 	list = malloc(sizeof(cond_av_list_t));
 	if (!list)
-		goto err;
+		return -1;
 	memset(list, 0, sizeof(cond_av_list_t));
 
 	list->node = node_ptr;
@@ -691,11 +691,6 @@ static int cond_insertf(avtab_t * a
 		data->tail->next = list;
 	data->tail = list;
 	return 0;
-
-      err:
-	cond_av_list_destroy(data->head);
-	data->head = NULL;
-	return -1;
 }
 
 static int cond_read_av_list(policydb_t * p, void *fp,
@@ -724,8 +719,10 @@ static int cond_read_av_list(policydb_t * p, void *fp,
 	for (i = 0; i < len; i++) {
 		rc = avtab_read_item(fp, p->policyvers, &p->te_cond_avtab,
 				     cond_insertf, &data);
-		if (rc)
+		if (rc) {
+			cond_av_list_destroy(data.head);
 			return rc;
+		}
 
 	}
 
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 13/36] libsepol: reject abnormal huge sid ids
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (11 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 12/36] libsepol: clean memory on conditional insertion failure Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 14/36] libsepol: reject invalid filetrans source type Christian Göttsche
                     ` (28 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Check if the sid value is saturated to guard dependent allocations.

    ==19967== ERROR: libFuzzer: out-of-memory (malloc(7784628224))
        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aabe3 in __asan::Allocator::Reallocate(void*, unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aabe3)
        #7 0x4aaa32 in __asan::asan_reallocarray(void*, unsigned long, unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aaa32)
        #8 0x525f8e in __interceptor_reallocarray (./out/binpolicy-fuzzer+0x525f8e)
        #9 0x5ebad3 in strs_add_at_index ./libsepol/src/kernel_to_common.c:224:9
        #10 0x5680eb in write_sids_to_conf ./libsepol/src/kernel_to_conf.c:466:8
        #11 0x55c1c0 in write_sid_decl_rules_to_conf ./libsepol/src/kernel_to_conf.c:498:8
        #12 0x55ad36 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3083:7
        #13 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #14 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #15 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #16 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #17 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #18 0x7f085ac657ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #19 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 1408405d..1868af5b 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -2883,6 +2883,8 @@ static int ocontext_read_xen(const struct policydb_compat_info *info,
 				if (rc < 0)
 					return -1;
 				c->sid[0] = le32_to_cpu(buf[0]);
+				if (is_saturated(c->sid[0]))
+					return -1;
 				if (context_read_and_validate
 				    (&c->context[0], p, fp))
 					return -1;
@@ -2994,6 +2996,8 @@ static int ocontext_read_selinux(const struct policydb_compat_info *info,
 				if (rc < 0)
 					return -1;
 				c->sid[0] = le32_to_cpu(buf[0]);
+				if (is_saturated(c->sid[0]))
+					return -1;
 				if (context_read_and_validate
 				    (&c->context[0], p, fp))
 					return -1;
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 14/36] libsepol: reject invalid filetrans source type
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (12 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 13/36] libsepol: reject abnormal huge sid ids Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 15/36] libsepol: zero member before potential dereference Christian Göttsche
                     ` (27 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Avoid integer underflow on invalid filetrans source types.

    policydb.c:2658:47: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'unsigned int'
        #0 0x4cf4cb in policydb_filetrans_insert ./libsepol/src/policydb.c:2658:47
        #1 0x4d221a in filename_trans_read_one_compat ./libsepol/src/policydb.c:2691:7
        #2 0x4d221a in filename_trans_read ./libsepol/src/policydb.c:2842:9
        #3 0x4d1370 in policydb_read ./libsepol/src/policydb.c:4447:7
        #4 0x4b1ee3 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:35:6
        #5 0x43f2f3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #6 0x42ae32 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #7 0x430d5b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #8 0x45a1f2 in main (./out/binpolicy-fuzzer+0x45a1f2)
        #9 0x7f8b8923a7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #10 0x407aa9 in _start (./out/binpolicy-fuzzer+0x407aa9)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 1868af5b..ab303ce6 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -2683,7 +2683,10 @@ static int filename_trans_read_one_compat(policydb_t *p, struct policy_file *fp)
 	if (rc < 0)
 		goto err;
 
-	stype  = le32_to_cpu(buf[0]);
+	stype = le32_to_cpu(buf[0]);
+	if (stype == 0)
+		goto err;
+
 	ttype  = le32_to_cpu(buf[1]);
 	tclass = le32_to_cpu(buf[2]);
 	otype  = le32_to_cpu(buf[3]);
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 15/36] libsepol: zero member before potential dereference
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (13 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 14/36] libsepol: reject invalid filetrans source type Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 16/36] libsepol: use size_t for indexes in strs helpers Christian Göttsche
                     ` (26 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

The `next` member might be checked against NULL and dereferenced before
it gets assigned, due to jumps from failure gotos to the cleanup
section.

    ==31017==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x000000579654 bp 0x7ffd3a07d110 sp 0x7ffd3a07d000 T0)
    ==31017==The signal is caused by a READ memory access.
    ==31017==Hint: this fault was caused by a dereference of a high value address (see register values below).  Disassemble the provided pc to learn which register was used.
        #0 0x579654 in filename_trans_read_one ./libsepol/src/policydb.c:2874:55
        #1 0x579654 in filename_trans_read ./libsepol/src/policydb.c:2902:9
        #2 0x5771b7 in policydb_read ./libsepol/src/policydb.c:4509:7
        #3 0x55a1f5 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:24:6
        #4 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #5 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #6 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #7 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #8 0x7f2a4e7f97ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #9 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index ab303ce6..c4dc3387 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -2780,6 +2780,7 @@ static int filename_trans_read_one(policydb_t *p, struct policy_file *fp)
 		if (!datum)
 			goto err;
 
+		datum->next = NULL;
 		*dst = datum;
 
 		/* ebitmap_read() will at least init the bitmap */
@@ -2797,7 +2798,6 @@ static int filename_trans_read_one(policydb_t *p, struct policy_file *fp)
 
 		dst = &datum->next;
 	}
-	*dst = NULL;
 
 	if (ndatum > 1 && filename_trans_check_datum(first))
 		goto err;
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 16/36] libsepol: use size_t for indexes in strs helpers
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (14 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 15/36] libsepol: zero member before potential dereference Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 17/36] libsepol: do not underflow on short format arguments Christian Göttsche
                     ` (25 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Use size_t, as the strs struct uses it for its size member.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_common.c | 8 ++++----
 libsepol/src/kernel_to_common.h | 4 ++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c
index 51df8c25..47c02d61 100644
--- a/libsepol/src/kernel_to_common.c
+++ b/libsepol/src/kernel_to_common.c
@@ -159,7 +159,7 @@ int strs_add(struct strs *strs, char *s)
 {
 	if (strs->num + 1 > strs->size) {
 		char **new;
-		unsigned i = strs->size;
+		size_t i = strs->size;
 		strs->size *= 2;
 		new = reallocarray(strs->list, strs->size, sizeof(char *));
 		if (!new) {
@@ -212,11 +212,11 @@ char *strs_remove_last(struct strs *strs)
 	return strs->list[strs->num];
 }
 
-int strs_add_at_index(struct strs *strs, char *s, unsigned index)
+int strs_add_at_index(struct strs *strs, char *s, size_t index)
 {
 	if (index >= strs->size) {
 		char **new;
-		unsigned i = strs->size;
+		size_t i = strs->size;
 		while (index >= strs->size) {
 			strs->size *= 2;
 		}
@@ -237,7 +237,7 @@ int strs_add_at_index(struct strs *strs, char *s, unsigned index)
 	return 0;
 }
 
-char *strs_read_at_index(struct strs *strs, unsigned index)
+char *strs_read_at_index(struct strs *strs, size_t index)
 {
 	if (index >= strs->num) {
 		return NULL;
diff --git a/libsepol/src/kernel_to_common.h b/libsepol/src/kernel_to_common.h
index 8aa483fa..e9932d30 100644
--- a/libsepol/src/kernel_to_common.h
+++ b/libsepol/src/kernel_to_common.h
@@ -99,8 +99,8 @@ int strs_add(struct strs *strs, char *s);
 __attribute__ ((format(printf, 2, 4)))
 int strs_create_and_add(struct strs *strs, const char *fmt, int num, ...);
 char *strs_remove_last(struct strs *strs);
-int strs_add_at_index(struct strs *strs, char *s, unsigned index);
-char *strs_read_at_index(struct strs *strs, unsigned index);
+int strs_add_at_index(struct strs *strs, char *s, size_t index);
+char *strs_read_at_index(struct strs *strs, size_t index);
 void strs_sort(struct strs *strs);
 unsigned strs_num_items(struct strs *strs);
 size_t strs_len_items(struct strs *strs);
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 17/36] libsepol: do not underflow on short format arguments
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (15 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 16/36] libsepol: use size_t for indexes in strs helpers Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 18/36] libsepol: do not crash on class gaps Christian Göttsche
                     ` (24 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Handle format arguments that do not have a size of at least 2.

    kernel_to_common.c:69:20: runtime error: unsigned integer overflow: 1 - 2 cannot be represented in type 'unsigned long'
        #0 0x557b0b in create_str_helper ./libsepol/src/kernel_to_common.c:69:20
        #1 0x5577b8 in create_str ./libsepol/src/kernel_to_common.c:99:8
        #2 0x56448c in cond_expr_to_str ./libsepol/src/kernel_to_conf.c:82:15
        #3 0x56448c in write_cond_nodes_to_conf ./libsepol/src/kernel_to_conf.c:2103:10
        #4 0x55bd9b in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3171:7
        #5 0x4f9d79 in main ./checkpolicy/checkpolicy.c:684:11
        #6 0x7fe2a342b7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #7 0x41f3a9 in _start (./checkpolicy/checkpolicy+0x41f3a9)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_common.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c
index 47c02d61..152f2816 100644
--- a/libsepol/src/kernel_to_common.c
+++ b/libsepol/src/kernel_to_common.c
@@ -57,7 +57,7 @@ static char *create_str_helper(const char *fmt, int num, va_list vargs)
 	va_list vargs2;
 	char *str = NULL;
 	char *s;
-	size_t len;
+	size_t len, s_len;
 	int i, rc;
 
 	va_copy(vargs2, vargs);
@@ -66,7 +66,8 @@ static char *create_str_helper(const char *fmt, int num, va_list vargs)
 
 	for (i=0; i<num; i++) {
 		s = va_arg(vargs, char *);
-		len += strlen(s) - 2; /* -2 for each %s in fmt */
+		s_len = strlen(s);
+		len += s_len > 1 ? s_len - 2 : 0; /* -2 for each %s in fmt */
 	}
 
 	str = malloc(len);
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 18/36] libsepol: do not crash on class gaps
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (16 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 17/36] libsepol: do not underflow on short format arguments Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 19/36] libsepol: do not crash on user gaps Christian Göttsche
                     ` (23 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Handle gaps in the class table while printing a policy configuration.

    ==21763==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000028 (pc 0x00000055b696 bp 0x7ffe69e8ab50 sp 0x7ffe69e8aa60 T0)
    ==21763==The signal is caused by a READ memory access.
    ==21763==Hint: address points to the zero page.
        #0 0x55b696 in constraint_rules_to_strs ./libsepol/src/kernel_to_conf.c:361:14
        #1 0x55ac80 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3063:7
        #2 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #3 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #4 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #5 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #6 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #7 0x7fc60d39e7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #8 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_cil.c  |  9 +++++++++
 libsepol/src/kernel_to_conf.c | 10 ++++++++--
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
index 305567a5..bb167647 100644
--- a/libsepol/src/kernel_to_cil.c
+++ b/libsepol/src/kernel_to_cil.c
@@ -358,6 +358,7 @@ static int constraint_rules_to_strs(struct policydb *pdb, struct strs *mls_strs,
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->constraints) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_constraint_rules_to_strs(pdb, name, class, class->constraints, mls_strs, non_mls_strs);
@@ -383,6 +384,7 @@ static int validatetrans_rules_to_strs(struct policydb *pdb, struct strs *mls_st
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->validatetrans) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_validatetrans_rules_to_strs(pdb, name, class->validatetrans, mls_strs, non_mls_strs);
@@ -461,6 +463,7 @@ static int write_class_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* class */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = pdb->p_class_val_to_name[i];
 		perms = class_or_common_perms_to_str(&class->permissions);
 		if (perms) {
@@ -488,6 +491,7 @@ static int write_class_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* classcommon */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = pdb->p_class_val_to_name[i];
 		if (class->comkey != NULL) {
 			sepol_printf(out, "(classcommon %s %s)\n", name, class->comkey);
@@ -503,6 +507,7 @@ static int write_class_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 	}
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = class->comkey;
 		if (name != NULL) {
 			common = hashtab_search(pdb->p_commons.table, name);
@@ -727,6 +732,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* default_user */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_user != 0) {
 			rc = write_default_user_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -738,6 +744,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* default_role */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_role != 0) {
 			rc = write_default_role_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -749,6 +756,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* default_type */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_type != 0) {
 			rc = write_default_type_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -764,6 +772,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* default_range */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_range) {
 			rc = write_default_range_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index eb72e4ac..b2a42606 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -358,7 +358,7 @@ static int constraint_rules_to_strs(struct policydb *pdb, struct strs *mls_strs,
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
-		if (class->constraints) {
+		if (class && class->constraints) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_constraint_rules_to_strs(pdb, name, class, class->constraints, mls_strs, non_mls_strs);
 			if (rc != 0) {
@@ -383,7 +383,7 @@ static int validatetrans_rules_to_strs(struct policydb *pdb, struct strs *mls_st
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
-		if (class->validatetrans) {
+		if (class && class->validatetrans) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_validatetrans_rules_to_strs(pdb, name, class->validatetrans, mls_strs, non_mls_strs);
 			if (rc != 0) {
@@ -551,6 +551,7 @@ static int write_class_and_common_rules_to_conf(FILE *out, struct policydb *pdb)
 	}
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = class->comkey;
 		if (!name) continue;
 		common = hashtab_search(pdb->p_commons.table, name);
@@ -577,6 +578,7 @@ static int write_class_and_common_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* class */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = pdb->p_class_val_to_name[i];
 		sepol_printf(out, "class %s", name);
 		if (class->comkey) {
@@ -702,6 +704,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* default_user */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_user != 0) {
 			rc = write_default_user_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -713,6 +716,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* default_role */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_role != 0) {
 			rc = write_default_role_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -724,6 +728,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* default_type */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_type != 0) {
 			rc = write_default_type_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -739,6 +744,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* default_range */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_range != 0) {
 			rc = write_default_range_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 19/36] libsepol: do not crash on user gaps
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (17 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 18/36] libsepol: do not crash on class gaps Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 20/36] libsepol: use correct size for initial string list Christian Göttsche
                     ` (22 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Handle gaps in the user table while printing a policy configuration.

    ==24424==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x0000004bdc55 bp 0x7ffc8790b810 sp 0x7ffc8790afb0 T0)
    ==24424==The signal is caused by a READ memory access.
    ==24424==Hint: address points to the zero page.
        #0 0x4bdc55 in __interceptor_strcmp (./out/binpolicy-fuzzer+0x4bdc55)
        #1 0x5ebdf6 in strs_cmp ./libsepol/src/kernel_to_common.c:253:9
        #2 0x505669 in __interceptor_qsort (./out/binpolicy-fuzzer+0x505669)
        #3 0x5ebd84 in strs_sort ./libsepol/src/kernel_to_common.c:261:2
        #4 0x564550 in write_user_decl_rules_to_conf ./libsepol/src/kernel_to_conf.c:2333:2
        #5 0x55b137 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3190:7
        #6 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #7 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #8 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #9 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #10 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #11 0x7f530128d7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #12 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_cil.c  | 1 +
 libsepol/src/kernel_to_conf.c | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
index bb167647..d9dc3f73 100644
--- a/libsepol/src/kernel_to_cil.c
+++ b/libsepol/src/kernel_to_cil.c
@@ -2392,6 +2392,7 @@ static int write_user_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 	}
 
 	for (i=0; i < pdb->p_users.nprim; i++) {
+		if (!pdb->p_user_val_to_name[i]) continue;
 		rc = strs_add(strs, pdb->p_user_val_to_name[i]);
 		if (rc != 0) {
 			goto exit;
diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index b2a42606..68dd2d32 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -2324,6 +2324,7 @@ static int write_user_decl_rules_to_conf(FILE *out, struct policydb *pdb)
 	}
 
 	for (i=0; i < pdb->p_users.nprim; i++) {
+		if (!pdb->p_user_val_to_name[i]) continue;
 		rc = strs_add(strs, pdb->p_user_val_to_name[i]);
 		if (rc != 0) {
 			goto exit;
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 20/36] libsepol: use correct size for initial string list
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (18 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 19/36] libsepol: do not crash on user gaps Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 21/36] libsepol: do not create a string list with initial size zero Christian Göttsche
                     ` (21 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Use the number of categories not levels, which might be zero, for the
string list initial size of categories.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_conf.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index 68dd2d32..dcdd4252 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -914,7 +914,7 @@ static int write_category_rules_to_conf(FILE *out, struct policydb *pdb)
 	unsigned i, j, num;
 	int rc = 0;
 
-	rc = strs_init(&strs, pdb->p_levels.nprim);
+	rc = strs_init(&strs, pdb->p_cats.nprim);
 	if (rc != 0) {
 		goto exit;
 	}
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 21/36] libsepol: do not create a string list with initial size zero
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (19 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 20/36] libsepol: use correct size for initial string list Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 22/36] libsepol: split validation of datum array gaps and entries Christian Göttsche
                     ` (20 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Currently is it implementation defined, due to the size being passed to
calloc(3), whether the operations fails nor not.
Also strs_add() does not handle a size of zero, cause it just multiplies
the size by two.

Use a default size of 1 if 0 is passed and swap the calloc arguments for
consistency.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_common.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c
index 152f2816..9f5400c9 100644
--- a/libsepol/src/kernel_to_common.c
+++ b/libsepol/src/kernel_to_common.c
@@ -107,6 +107,10 @@ int strs_init(struct strs **strs, size_t size)
 {
 	struct strs *new;
 
+	if (size == 0) {
+		size = 1;
+	}
+
 	*strs = NULL;
 
 	new = malloc(sizeof(struct strs));
@@ -115,7 +119,7 @@ int strs_init(struct strs **strs, size_t size)
 		return -1;
 	}
 
-	new->list = calloc(sizeof(char *), size);
+	new->list = calloc(size, sizeof(char *));
 	if (!new->list) {
 		sepol_log_err("Out of memory");
 		free(new);
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 22/36] libsepol: split validation of datum array gaps and entries
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (20 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 21/36] libsepol: do not create a string list with initial size zero Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 23/36] libsepol: validate MLS levels Christian Göttsche
                     ` (19 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Split the validation of array datums regarding their gaps and entries to
simplify further checking of common classes, booleans, levels and
categories.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 112 ++++++++++++++++++++-----------
 1 file changed, 73 insertions(+), 39 deletions(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 5804d247..d4dfab5c 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -6,11 +6,19 @@
 #include "debug.h"
 #include "policydb_validate.h"
 
+#define bool_xor(a, b) (!(a) != !(b))
+#define bool_xnor(a, b) !bool_xor(a, b)
+
 typedef struct validate {
 	uint32_t nprim;
 	ebitmap_t gaps;
 } validate_t;
 
+typedef struct map_arg {
+	validate_t *flavors;
+	sepol_handle_t *handle;
+	int mls;
+} map_arg_t;
 
 static int create_gap_ebitmap(char **val_to_name, uint32_t nprim, ebitmap_t *gaps)
 {
@@ -211,6 +219,13 @@ bad:
 	return -1;
 }
 
+static int validate_class_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_class_datum(margs->handle, d, margs->flavors);
+}
+
 static int validate_role_datum(sepol_handle_t *handle, role_datum_t *role, validate_t flavors[])
 {
 	if (validate_value(role->s.value, &flavors[SYM_ROLES]))
@@ -231,6 +246,13 @@ bad:
 	return -1;
 }
 
+static int validate_role_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_role_datum(margs->handle, d, margs->flavors);
+}
+
 static int validate_type_datum(sepol_handle_t *handle, type_datum_t *type, validate_t flavors[])
 {
 	if (validate_value(type->s.value, &flavors[SYM_TYPES]))
@@ -247,6 +269,13 @@ bad:
 	return -1;
 }
 
+static int validate_type_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_type_datum(margs->handle, d, margs->flavors);
+}
+
 static int validate_mls_semantic_cat(mls_semantic_cat_t *cat, validate_t *cats)
 {
 	for (; cat; cat = cat->next) {
@@ -310,32 +339,25 @@ bad:
 	return -1;
 }
 
-static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+static int validate_user_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_user_datum(margs->handle, d, margs->flavors);
+}
+
+static int validate_datum_array_gaps(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
 {
 	unsigned int i;
 
 	for (i = 0; i < p->p_classes.nprim; i++) {
-		if (p->class_val_to_struct[i]) {
-			if (ebitmap_get_bit(&flavors[SYM_CLASSES].gaps, i))
-				goto bad;
-			if (validate_class_datum(handle, p->class_val_to_struct[i], flavors))
-				goto bad;
-		} else {
-			if (!ebitmap_get_bit(&flavors[SYM_CLASSES].gaps, i))
-				goto bad;
-		}
+		if (bool_xnor(p->class_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_CLASSES].gaps, i)))
+			goto bad;
 	}
 
 	for (i = 0; i < p->p_roles.nprim; i++) {
-		if (p->role_val_to_struct[i]) {
-			if (ebitmap_get_bit(&flavors[SYM_ROLES].gaps, i))
-				goto bad;
-			if (validate_role_datum(handle, p->role_val_to_struct[i], flavors))
-				goto bad;
-		} else {
-			if (!ebitmap_get_bit(&flavors[SYM_ROLES].gaps, i))
-				goto bad;
-		}
+		if (bool_xnor(p->role_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_ROLES].gaps, i)))
+			goto bad;
 	}
 
 	/*
@@ -344,34 +366,43 @@ static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate
 	 */
 	if (p->policyvers < POLICYDB_VERSION_AVTAB || p->policyvers > POLICYDB_VERSION_PERMISSIVE) {
 		for (i = 0; i < p->p_types.nprim; i++) {
-			if (p->type_val_to_struct[i]) {
-				if (ebitmap_get_bit(&flavors[SYM_TYPES].gaps, i))
-					goto bad;
-				if (validate_type_datum(handle, p->type_val_to_struct[i], flavors))
-					goto bad;
-			} else {
-				if (!ebitmap_get_bit(&flavors[SYM_TYPES].gaps, i))
-					goto bad;
-			}
+			if (bool_xnor(p->type_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_TYPES].gaps, i)))
+				goto bad;
 		}
 	}
 
 	for (i = 0; i < p->p_users.nprim; i++) {
-		if (p->user_val_to_struct[i]) {
-			if (ebitmap_get_bit(&flavors[SYM_USERS].gaps, i))
-				goto bad;
-			if (validate_user_datum(handle, p->user_val_to_struct[i], flavors))
-				goto bad;
-		} else {
-			if (!ebitmap_get_bit(&flavors[SYM_USERS].gaps, i))
-				goto bad;
-		}
+		if (bool_xnor(p->user_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_USERS].gaps, i)))
+			goto bad;
 	}
 
 	return 0;
 
 bad:
-	ERR(handle, "Invalid datum arrays");
+	ERR(handle, "Invalid datum array gaps");
+	return -1;
+}
+
+static int validate_datum_array_entries(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	map_arg_t margs = { flavors, handle, p->mls };
+
+	if (hashtab_map(p->p_classes.table, validate_class_datum_wrapper, &margs))
+		goto bad;
+
+	if (hashtab_map(p->p_roles.table, validate_role_datum_wrapper, &margs))
+		goto bad;
+
+	if (hashtab_map(p->p_types.table, validate_type_datum_wrapper, &margs))
+		goto bad;
+
+	if (hashtab_map(p->p_users.table, validate_user_datum_wrapper, &margs))
+		goto bad;
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid datum array entries");
 	return -1;
 }
 
@@ -762,7 +793,10 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 	if (validate_scopes(handle, p->scope, p->global))
 		goto bad;
 
-	if (validate_datum_arrays(handle, p, flavors))
+	if (validate_datum_array_gaps(handle, p, flavors))
+		goto bad;
+
+	if (validate_datum_array_entries(handle, p, flavors))
 		goto bad;
 
 	validate_array_destroy(flavors);
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 23/36] libsepol: validate MLS levels
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (21 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 22/36] libsepol: split validation of datum array gaps and entries Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 24/36] libsepol: validate expanded user range and level Christian Göttsche
                     ` (18 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Validate the level map of the policy to ensure no level refers to a non
existent category.

READ of size 8 at 0x602000000c58 thread T0
    #0 0x568d2c in cats_ebitmap_len ./libsepol/src/kernel_to_conf.c:1003:14
    #1 0x568d2c in cats_ebitmap_to_str ./libsepol/src/kernel_to_conf.c:1038:19
    #2 0x55e371 in write_level_rules_to_conf ./libsepol/src/kernel_to_conf.c:1106:11
    #3 0x55e371 in write_mls_rules_to_conf ./libsepol/src/kernel_to_conf.c:1140:7
    #4 0x55adb1 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3103:7
    #5 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
    #6 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
    #7 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
    #8 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
    #9 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
    #10 0x7f741d0d67ec in __libc_start_main csu/../csu/libc-start.c:332:16
    #11 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index d4dfab5c..03ab4445 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -319,6 +319,27 @@ bad:
 	return -1;
 }
 
+static int validate_mls_level(mls_level_t *level, validate_t *sens, validate_t *cats)
+{
+	if (validate_value(level->sens, sens))
+		goto bad;
+	if (validate_ebitmap(&level->cat, cats))
+		goto bad;
+
+	return 0;
+
+	bad:
+	return -1;
+}
+
+static int validate_level_datum(__attribute__ ((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	level_datum_t *level = d;
+	validate_t *flavors = args;
+
+	return validate_mls_level(level->level, &flavors[SYM_LEVELS], &flavors[SYM_CATS]);
+}
+
 static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, validate_t flavors[])
 {
 	if (validate_value(user->s.value, &flavors[SYM_USERS]))
@@ -399,6 +420,9 @@ static int validate_datum_array_entries(sepol_handle_t *handle, policydb_t *p, v
 	if (hashtab_map(p->p_users.table, validate_user_datum_wrapper, &margs))
 		goto bad;
 
+	if (p->mls && hashtab_map(p->p_levels.table, validate_level_datum, flavors))
+		goto bad;
+
 	return 0;
 
 bad:
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 24/36] libsepol: validate expanded user range and level
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (22 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 23/36] libsepol: validate MLS levels Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 25/36] libsepol: validate permission count of classes Christian Göttsche
                     ` (17 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Check those contains valid values.

    ==57532==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000001178 at pc 0x000000564c04 bp 0x7ffed7a5ad90 sp 0x7ffed7a5ad88
    READ of size 8 at 0x603000001178 thread T0
        #0 0x564c03 in level_to_str ./libsepol/src/kernel_to_conf.c:1901:19
        #1 0x564c03 in range_to_str ./libsepol/src/kernel_to_conf.c:1926:9
        #2 0x564c03 in write_user_decl_rules_to_conf ./libsepol/src/kernel_to_conf.c:2367:12
        #3 0x55b137 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3184:7
        #4 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #5 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #6 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #7 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #8 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #9 0x7f2c2e1a77ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #10 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 03ab4445..adaa3fb2 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -340,7 +340,20 @@ static int validate_level_datum(__attribute__ ((unused)) hashtab_key_t k, hashta
 	return validate_mls_level(level->level, &flavors[SYM_LEVELS], &flavors[SYM_CATS]);
 }
 
-static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, validate_t flavors[])
+static int validate_mls_range(mls_range_t *range, validate_t *sens, validate_t *cats)
+{
+	if (validate_mls_level(&range->level[0], sens, cats))
+		goto bad;
+	if (validate_mls_level(&range->level[1], sens, cats))
+		goto bad;
+
+	return 0;
+
+	bad:
+	return -1;
+}
+
+static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, validate_t flavors[], int mls)
 {
 	if (validate_value(user->s.value, &flavors[SYM_USERS]))
 		goto bad;
@@ -350,6 +363,10 @@ static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, valid
 		goto bad;
 	if (validate_mls_semantic_level(&user->dfltlevel, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
 		goto bad;
+	if (mls && validate_mls_range(&user->exp_range, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		goto bad;
+	if (mls && validate_mls_level(&user->exp_dfltlevel, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		goto bad;
 	if (user->bounds && validate_value(user->bounds, &flavors[SYM_USERS]))
 		goto bad;
 
@@ -364,7 +381,7 @@ static int validate_user_datum_wrapper(__attribute__((unused)) hashtab_key_t k,
 {
 	map_arg_t *margs = args;
 
-	return validate_user_datum(margs->handle, d, margs->flavors);
+	return validate_user_datum(margs->handle, d, margs->flavors, margs->mls);
 }
 
 static int validate_datum_array_gaps(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 25/36] libsepol: validate permission count of classes
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (23 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 24/36] libsepol: validate expanded user range and level Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 26/36] libsepol: resolve log message mismatch Christian Göttsche
                     ` (16 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Check a common class or a class together with its common class parent
does not have more than the supported 32 permissions.

    ==28413==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f74ec3341a3 bp 0x7ffd0b7e5030 sp 0x7ffd0b7e47e8 T0)
    ==28413==The signal is caused by a READ memory access.
    ==28413==Hint: address points to the zero page.
        #0 0x7f74ec3341a3  string/../sysdeps/x86_64/multiarch/../strchr.S:32
        #1 0x4bfc78 in strchr (./out/binpolicy-fuzzer+0x4bfc78)
        #2 0x55b7f2 in class_constraint_rules_to_strs ./libsepol/src/kernel_to_conf.c:288:7
        #3 0x55b7f2 in constraint_rules_to_strs ./libsepol/src/kernel_to_conf.c:364:9
        #4 0x55ac80 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3071:7
        #5 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #6 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #7 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #8 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #9 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #10 0x7f74ec2be7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #11 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   also check common classes
---
 libsepol/src/policydb_validate.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index adaa3fb2..e8d70585 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -211,6 +211,8 @@ static int validate_class_datum(sepol_handle_t *handle, class_datum_t *class, va
 		goto bad;
 	if (validate_constraint_nodes(handle, class->validatetrans, flavors))
 		goto bad;
+	if (class->permissions.nprim > PERM_SYMTAB_SIZE)
+		goto bad;
 
 	return 0;
 
@@ -226,6 +228,25 @@ static int validate_class_datum_wrapper(__attribute__((unused)) hashtab_key_t k,
 	return validate_class_datum(margs->handle, d, margs->flavors);
 }
 
+static int validate_common_datum(sepol_handle_t *handle, common_datum_t *common)
+{
+	if (common->permissions.nprim > PERM_SYMTAB_SIZE)
+		goto bad;
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid common class datum");
+	return -1;
+}
+
+static int validate_common_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_common_datum(margs->handle, d);
+}
+
 static int validate_role_datum(sepol_handle_t *handle, role_datum_t *role, validate_t flavors[])
 {
 	if (validate_value(role->s.value, &flavors[SYM_ROLES]))
@@ -425,6 +446,9 @@ static int validate_datum_array_entries(sepol_handle_t *handle, policydb_t *p, v
 {
 	map_arg_t margs = { flavors, handle, p->mls };
 
+	if (hashtab_map(p->p_commons.table, validate_common_datum_wrapper, &margs))
+		goto bad;
+
 	if (hashtab_map(p->p_classes.table, validate_class_datum_wrapper, &margs))
 		goto bad;
 
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 26/36] libsepol: resolve log message mismatch
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (24 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 25/36] libsepol: validate permission count of classes Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 27/36] libsepol: validate avtab and avrule types Christian Göttsche
                     ` (15 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index e8d70585..82193379 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -263,7 +263,7 @@ static int validate_role_datum(sepol_handle_t *handle, role_datum_t *role, valid
 	return 0;
 
 bad:
-	ERR(handle, "Invalid class datum");
+	ERR(handle, "Invalid role datum");
 	return -1;
 }
 
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 27/36] libsepol: validate avtab and avrule types
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (25 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 26/36] libsepol: resolve log message mismatch Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 28/36] libsepol: validate constraint expression operators and attributes Christian Göttsche
                     ` (14 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Check for invalid avtab or avrule types.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   also check avrule types
---
 libsepol/src/policydb_validate.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 82193379..5ef95c61 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -483,6 +483,20 @@ static int validate_avtab_key(avtab_key_t *key, validate_t flavors[])
 		goto bad;
 	if (validate_value(key->target_class, &flavors[SYM_CLASSES]))
 		goto bad;
+	switch (0xFFF & key->specified) {
+	case AVTAB_ALLOWED:
+	case AVTAB_AUDITALLOW:
+	case AVTAB_AUDITDENY:
+	case AVTAB_XPERMS_ALLOWED:
+	case AVTAB_XPERMS_AUDITALLOW:
+	case AVTAB_XPERMS_DONTAUDIT:
+	case AVTAB_TRANSITION:
+	case AVTAB_MEMBER:
+	case AVTAB_CHANGE:
+		break;
+	default:
+		goto bad;
+	}
 
 	return 0;
 
@@ -536,6 +550,23 @@ static int validate_avrules(sepol_handle_t *handle, avrule_t *avrule, validate_t
 			if (validate_value(class->tclass, &flavors[SYM_CLASSES]))
 				goto bad;
 		}
+		switch(avrule->specified) {
+		case AVRULE_ALLOWED:
+		case AVRULE_AUDITALLOW:
+		case AVRULE_AUDITDENY:
+		case AVRULE_DONTAUDIT:
+		case AVRULE_NEVERALLOW:
+		case AVRULE_TRANSITION:
+		case AVRULE_MEMBER:
+		case AVRULE_CHANGE:
+		case AVRULE_XPERMS_ALLOWED:
+		case AVRULE_XPERMS_AUDITALLOW:
+		case AVRULE_XPERMS_DONTAUDIT:
+		case AVRULE_XPERMS_NEVERALLOW:
+			break;
+		default:
+			goto bad;
+		}
 	}
 
 	return 0;
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 28/36] libsepol: validate constraint expression operators and attributes
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (26 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 27/36] libsepol: validate avtab and avrule types Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 29/36] libsepol: validate type of avtab type rules Christian Göttsche
                     ` (13 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 43 ++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 5ef95c61..25c6f0db 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -193,6 +193,49 @@ static int validate_constraint_nodes(sepol_handle_t *handle, constraint_node_t *
 				if (validate_type_set(cexp->type_names, &flavors[SYM_TYPES]))
 					goto bad;
 			}
+
+			if (cexp->expr_type == CEXPR_ATTR || cexp->expr_type == CEXPR_NAMES) {
+				switch (cexp->op) {
+				case CEXPR_EQ:
+				case CEXPR_NEQ:
+				case CEXPR_DOM:
+				case CEXPR_DOMBY:
+				case CEXPR_INCOMP:
+					break;
+				default:
+					goto bad;
+				}
+
+				switch (cexp->attr) {
+				case CEXPR_USER:
+				case CEXPR_USER | CEXPR_TARGET:
+				case CEXPR_USER | CEXPR_XTARGET:
+				case CEXPR_ROLE:
+				case CEXPR_ROLE | CEXPR_TARGET:
+				case CEXPR_ROLE | CEXPR_XTARGET:
+				case CEXPR_TYPE:
+				case CEXPR_TYPE | CEXPR_TARGET:
+				case CEXPR_TYPE | CEXPR_XTARGET:
+				case CEXPR_L1L2:
+				case CEXPR_L1H2:
+				case CEXPR_H1L2:
+				case CEXPR_H1H2:
+				case CEXPR_L1H1:
+				case CEXPR_L2H2:
+					break;
+				default:
+					goto bad;
+				}
+			} else {
+				switch (cexp->expr_type) {
+				case CEXPR_NOT:
+				case CEXPR_AND:
+				case CEXPR_OR:
+					break;
+				default:
+					goto bad;
+				}
+			}
 		}
 	}
 
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 29/36] libsepol: validate type of avtab type rules
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (27 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 28/36] libsepol: validate constraint expression operators and attributes Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 30/36] libsepol: validate ocontexts Christian Göttsche
                     ` (12 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

    ==80903==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000005c0 at pc 0x0000005696c8 bp 0x7ffdb11ea560 sp 0x7ffdb11ea558
    READ of size 8 at 0x6020000005c0 thread T0
        #0 0x5696c7 in avtab_node_to_str ./libsepol/src/kernel_to_conf.c:1736:9
        #1 0x569013 in map_avtab_write_helper ./libsepol/src/kernel_to_conf.c:1767:10
        #2 0x5ab837 in avtab_map ./libsepol/src/avtab.c:347:10
        #3 0x561f9a in write_avtab_flavor_to_conf ./libsepol/src/kernel_to_conf.c:1798:7
        #4 0x561f9a in write_avtab_to_conf ./libsepol/src/kernel_to_conf.c:1819:8
        #5 0x55afba in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3159:7
        #6 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #7 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #8 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #9 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #10 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #11 0x7f97a83fd7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #12 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 25c6f0db..57eb2550 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -547,15 +547,22 @@ bad:
 	return -1;
 }
 
-static int validate_avtab_key_wrapper(avtab_key_t *k,  __attribute__ ((unused)) avtab_datum_t *d, void *args)
+static int validate_avtab_key_and_datum(avtab_key_t *k, avtab_datum_t *d, void *args)
 {
 	validate_t *flavors = (validate_t *)args;
-	return validate_avtab_key(k, flavors);
+
+	if (validate_avtab_key(k, flavors))
+		return -1;
+
+	if ((k->specified & AVTAB_TYPE) && validate_value(d->data, &flavors[SYM_TYPES]))
+		return -1;
+
+	return 0;
 }
 
 static int validate_avtab(sepol_handle_t *handle, avtab_t *avtab, validate_t flavors[])
 {
-	if (avtab_map(avtab, validate_avtab_key_wrapper, flavors)) {
+	if (avtab_map(avtab, validate_avtab_key_and_datum, flavors)) {
 		ERR(handle, "Invalid avtab");
 		return -1;
 	}
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 30/36] libsepol: validate ocontexts
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (28 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 29/36] libsepol: validate type of avtab type rules Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-09 19:04     ` James Carter
  2021-11-05 15:45   ` [RFC PATCH v2 31/36] libsepol: validate genfs contexts Christian Göttsche
                     ` (11 subsequent siblings)
  41 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Check the literal contexts in ocontext statements are defined.

    ==91274==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f60b0afe8c6 bp 0x7ffd42edc990 sp 0x7ffd42edc148 T0)
    ==91274==The signal is caused by a READ memory access.
    ==91274==Hint: address points to the zero page.
        #0 0x7f60b0afe8c6  string/../sysdeps/x86_64/multiarch/../strlen.S:120
        #1 0x4bd128 in __interceptor_strlen (./out/binpolicy-fuzzer+0x4bd128)
        #2 0x5eb387 in create_str_helper ./libsepol/src/kernel_to_common.c:69:10
        #3 0x5eb11e in create_str ./libsepol/src/kernel_to_common.c:99:8
        #4 0x56ad7b in context_to_str ./libsepol/src/kernel_to_conf.c:2408:9
        #5 0x56a717 in write_sid_context_rules_to_conf ./libsepol/src/kernel_to_conf.c:2441:9
        #6 0x55b26c in write_selinux_isid_rules_to_conf ./libsepol/src/kernel_to_conf.c:2476:9
        #7 0x55b26c in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3206:8
        #8 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #9 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #10 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #11 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #12 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #13 0x7f60b0a887ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #14 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   also check in base modules

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 44 ++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 57eb2550..96f133c9 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -736,6 +736,47 @@ static int validate_filename_trans_hashtab(sepol_handle_t *handle, hashtab_t fil
 	return 0;
 }
 
+static int validate_context(context_struct_t *con, validate_t flavors[], int mls)
+{
+	if (validate_value(con->user, &flavors[SYM_USERS]))
+		return -1;
+	if (validate_value(con->role, &flavors[SYM_ROLES]))
+		return -1;
+	if (validate_value(con->type, &flavors[SYM_TYPES]))
+		return -1;
+	if (mls && validate_mls_range(&con->range, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		return -1;
+
+	return 0;
+}
+
+static int validate_ocontexts(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	ocontext_t *octx;
+	unsigned int i;
+
+	for (i = 0; i < OCON_NUM; i++) {
+		for (octx = p->ocontexts[i]; octx; octx = octx->next) {
+			if (validate_context(&octx->context[0], flavors, p->mls))
+				goto bad;
+
+			switch (i) {
+			case OCON_FS:
+			case OCON_NETIF:
+				if (validate_context(&octx->context[1], flavors, p->mls))
+					goto bad;
+				break;
+			}
+		}
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid ocontext");
+	return -1;
+}
+
 /*
  * Functions to validate a module policydb
  */
@@ -936,6 +977,9 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 			goto bad;
 	}
 
+	if (validate_ocontexts(handle, p, flavors))
+		goto bad;
+
 	if (validate_scopes(handle, p->scope, p->global))
 		goto bad;
 
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 31/36] libsepol: validate genfs contexts
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (29 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 30/36] libsepol: validate ocontexts Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 32/36] libsepol: validate permissive types Christian Göttsche
                     ` (10 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Check the literal contexts in a genfs statement are defined.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   also check in base modules
---
 libsepol/src/policydb_validate.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 96f133c9..5cec143f 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -777,6 +777,25 @@ bad:
 	return -1;
 }
 
+static int validate_genfs(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	genfs_t *genfs;
+	ocontext_t *octx;
+
+	for (genfs = p->genfs; genfs; genfs = genfs->next) {
+		for (octx = genfs->head; octx; octx = octx->next) {
+			if (validate_context(&octx->context[0], flavors, p->mls))
+				goto bad;
+		}
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid genfs");
+	return -1;
+}
+
 /*
  * Functions to validate a module policydb
  */
@@ -980,6 +999,9 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 	if (validate_ocontexts(handle, p, flavors))
 		goto bad;
 
+	if (validate_genfs(handle, p, flavors))
+		goto bad;
+
 	if (validate_scopes(handle, p->scope, p->global))
 		goto bad;
 
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 32/36] libsepol: validate permissive types
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (30 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 31/36] libsepol: validate genfs contexts Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 33/36] libsepol: validate policy properties Christian Göttsche
                     ` (9 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 5cec143f..ffa21ee1 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -959,6 +959,23 @@ bad:
 	return -1;
 }
 
+static int validate_permissives(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	ebitmap_node_t *node;
+	unsigned i;
+
+	ebitmap_for_each_positive_bit(&p->permissive_map, node, i) {
+		if (validate_value(i, &flavors[SYM_TYPES]))
+			goto bad;
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid permissive type");
+	return -1;
+}
+
 static void validate_array_destroy(validate_t flavors[])
 {
 	unsigned int i;
@@ -1011,6 +1028,9 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 	if (validate_datum_array_entries(handle, p, flavors))
 		goto bad;
 
+	if (validate_permissives(handle, p, flavors))
+		goto bad;
+
 	validate_array_destroy(flavors);
 
 	return 0;
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 33/36] libsepol: validate policy properties
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (31 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 32/36] libsepol: validate permissive types Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 34/36] libsepol: validate categories Christian Göttsche
                     ` (8 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 51 ++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index ffa21ee1..27f25132 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -976,6 +976,54 @@ bad:
 	return -1;
 }
 
+static int validate_properties(sepol_handle_t *handle, policydb_t *p)
+{
+	switch (p->policy_type) {
+	case POLICY_KERN:
+		if (p->policyvers < POLICYDB_VERSION_MIN || p->policyvers > POLICYDB_VERSION_MAX)
+			goto bad;
+		break;
+	case POLICY_BASE:
+	case POLICY_MOD:
+		if (p->policyvers < MOD_POLICYDB_VERSION_MIN || p->policyvers > MOD_POLICYDB_VERSION_MAX)
+			goto bad;
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (p->target_platform) {
+	case SEPOL_TARGET_SELINUX:
+	case SEPOL_TARGET_XEN:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (p->mls) {
+	case 0:
+	case 1:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (p->handle_unknown) {
+	case SEPOL_DENY_UNKNOWN:
+	case SEPOL_REJECT_UNKNOWN:
+	case SEPOL_ALLOW_UNKNOWN:
+		break;
+	default:
+		goto bad;
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid policy property");
+	return -1;
+}
+
 static void validate_array_destroy(validate_t flavors[])
 {
 	unsigned int i;
@@ -995,6 +1043,9 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 	if (validate_array_init(p, flavors))
 		goto bad;
 
+	if (validate_properties(handle, p))
+		goto bad;
+
 	if (p->policy_type == POLICY_KERN) {
 		if (validate_avtab(handle, &p->te_avtab, flavors))
 			goto bad;
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 34/36] libsepol: validate categories
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (32 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 33/36] libsepol: validate policy properties Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 35/36] libsepol: validate fsuse types Christian Göttsche
                     ` (7 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Check all categories have valid values, especially important for
aliases.

        ==7888==ERROR: AddressSanitizer: SEGV on unknown address 0x602000400710 (pc 0x00000055debc bp 0x7ffe0ff2a9d0 sp 0x7ffe0ff2a8e0 T0)
        ==7888==The signal is caused by a READ memory access.
        #0 0x55debc in write_category_rules_to_conf ./libsepol/src/kernel_to_conf.c:946:9
        #1 0x55debc in write_mls_rules_to_conf ./libsepol/src/kernel_to_conf.c:1137:7
        #2 0x55adb1 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3106:7
        #3 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:37:9
        #4 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #5 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #6 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #7 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #8 0x7fe80ccaf7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #9 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 27f25132..9b18ac68 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -485,6 +485,14 @@ bad:
 	return -1;
 }
 
+static int validate_datum(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	symtab_datum_t *s = d;
+	uint32_t *nprim = (uint32_t *)args;
+
+	return !value_isvalid(s->value, *nprim);
+}
+
 static int validate_datum_array_entries(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
 {
 	map_arg_t margs = { flavors, handle, p->mls };
@@ -507,6 +515,9 @@ static int validate_datum_array_entries(sepol_handle_t *handle, policydb_t *p, v
 	if (p->mls && hashtab_map(p->p_levels.table, validate_level_datum, flavors))
 		goto bad;
 
+	if (hashtab_map(p->p_cats.table, validate_datum, &flavors[SYM_CATS]))
+		goto bad;
+
 	return 0;
 
 bad:
@@ -903,14 +914,6 @@ bad:
 	return -1;
 }
 
-static int validate_datum(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args)
-{
-	symtab_datum_t *s = d;
-	uint32_t *nprim = (uint32_t *)args;
-
-	return !value_isvalid(s->value, *nprim);
-}
-
 static int validate_symtabs(sepol_handle_t *handle, symtab_t symtabs[], validate_t flavors[])
 {
 	unsigned int i;
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 35/36] libsepol: validate fsuse types
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (33 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 34/36] libsepol: validate categories Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 36/36] libsepol: validate class default targets Christian Göttsche
                     ` (6 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Check the fsuse type is valid, e.g. of type xattr, trans or task.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   do not reject in binary reading, but check at validation step
---
 libsepol/src/policydb_validate.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 9b18ac68..1c5ca0dd 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -2,6 +2,7 @@
 #include <sepol/policydb/conditional.h>
 #include <sepol/policydb/ebitmap.h>
 #include <sepol/policydb/policydb.h>
+#include <sepol/policydb/services.h>
 
 #include "debug.h"
 #include "policydb_validate.h"
@@ -777,6 +778,15 @@ static int validate_ocontexts(sepol_handle_t *handle, policydb_t *p, validate_t
 				if (validate_context(&octx->context[1], flavors, p->mls))
 					goto bad;
 				break;
+			case OCON_FSUSE:
+				switch (octx->v.behavior) {
+				case SECURITY_FS_USE_XATTR:
+				case SECURITY_FS_USE_TRANS:
+				case SECURITY_FS_USE_TASK:
+					break;
+				default:
+					goto bad;
+				}
 			}
 		}
 	}
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 36/36] libsepol: validate class default targets
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (34 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 35/36] libsepol: validate fsuse types Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 37/40] [WIP] libsepol: export policydb_validate Christian Göttsche
                     ` (5 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Check the class default targets are valid values, e.g. source or target
for user, role and type.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   do not reject in binary reading, but check at validation step
---
 libsepol/src/policydb_validate.c | 41 ++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 1c5ca0dd..b7868fbe 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -258,6 +258,47 @@ static int validate_class_datum(sepol_handle_t *handle, class_datum_t *class, va
 	if (class->permissions.nprim > PERM_SYMTAB_SIZE)
 		goto bad;
 
+	switch (class->default_user) {
+	case 0:
+	case DEFAULT_SOURCE:
+	case DEFAULT_TARGET:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (class->default_role) {
+	case 0:
+	case DEFAULT_SOURCE:
+	case DEFAULT_TARGET:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (class->default_type) {
+	case 0:
+	case DEFAULT_SOURCE:
+	case DEFAULT_TARGET:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (class->default_range) {
+	case 0:
+	case DEFAULT_SOURCE_LOW:
+	case DEFAULT_SOURCE_HIGH:
+	case DEFAULT_SOURCE_LOW_HIGH:
+	case DEFAULT_TARGET_LOW:
+	case DEFAULT_TARGET_HIGH:
+	case DEFAULT_TARGET_LOW_HIGH:
+	case DEFAULT_GLBLUB:
+		break;
+	default:
+		goto bad;
+	}
+
 	return 0;
 
 bad:
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 37/40] [WIP] libsepol: export policydb_validate
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (35 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 36/36] libsepol: validate class default targets Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 38/40] [WIP] checkpolicy: validate generated policies Christian Göttsche
                     ` (4 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

Export the interface to validate a policydb structure.
It can be used e.g. in compilers to verify they generate valid policies.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/include/sepol/policydb/validate.h | 17 +++++++++++++++++
 libsepol/src/policydb_validate.h           |  4 +---
 2 files changed, 18 insertions(+), 3 deletions(-)
 create mode 100644 libsepol/include/sepol/policydb/validate.h

diff --git a/libsepol/include/sepol/policydb/validate.h b/libsepol/include/sepol/policydb/validate.h
new file mode 100644
index 00000000..eff0779e
--- /dev/null
+++ b/libsepol/include/sepol/policydb/validate.h
@@ -0,0 +1,17 @@
+#ifndef _SEPOL_POLICYDB_VALIDATE_H
+#define _SEPOL_POLICYDB_VALIDATE_H
+
+#include <sepol/handle.h>
+#include <sepol/policydb/policydb.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int validate_policydb(sepol_handle_t *handle, policydb_t *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libsepol/src/policydb_validate.h b/libsepol/src/policydb_validate.h
index d9f7229b..c2980403 100644
--- a/libsepol/src/policydb_validate.h
+++ b/libsepol/src/policydb_validate.h
@@ -1,7 +1,5 @@
 #include <stdint.h>
 
-#include <sepol/handle.h>
-#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/validate.h>
 
 int value_isvalid(uint32_t value, uint32_t nprim);
-int validate_policydb(sepol_handle_t *handle, policydb_t *p);
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 38/40] [WIP] checkpolicy: validate generated policies
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (36 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 37/40] [WIP] libsepol: export policydb_validate Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 39/40] [CROSS-PATCH] libsepol: avoid passing NULL pointer to memcpy Christian Göttsche
                     ` (3 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

After generating policies validate them.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 checkpolicy/checkmodule.c | 8 ++++++++
 checkpolicy/checkpolicy.c | 6 ++++++
 2 files changed, 14 insertions(+)

diff --git a/checkpolicy/checkmodule.c b/checkpolicy/checkmodule.c
index 3432608b..846e5a36 100644
--- a/checkpolicy/checkmodule.c
+++ b/checkpolicy/checkmodule.c
@@ -29,6 +29,7 @@
 #include <sepol/policydb/expand.h>
 #include <sepol/policydb/link.h>
 #include <sepol/policydb/sidtab.h>
+#include <sepol/policydb/validate.h>
 
 #include "queue.h"
 #include "checkpolicy.h"
@@ -329,6 +330,13 @@ int main(int argc, char **argv)
 
 	sepol_sidtab_destroy(&sidtab);
 
+	modpolicydb.policyvers = policyvers;
+
+	if (validate_policydb(NULL, &modpolicydb)) {
+		fprintf(stderr, "%s:  validation of generated policy failed\n", argv[0]);
+		exit(1);
+	}
+
 	if (outfile) {
 		FILE *outfp = fopen(outfile, "w");
 
diff --git a/checkpolicy/checkpolicy.c b/checkpolicy/checkpolicy.c
index 926ce72c..3ce63d06 100644
--- a/checkpolicy/checkpolicy.c
+++ b/checkpolicy/checkpolicy.c
@@ -87,6 +87,7 @@
 #include <sepol/policydb/hierarchy.h>
 #include <sepol/policydb/expand.h>
 #include <sepol/policydb/link.h>
+#include <sepol/policydb/validate.h>
 
 #include "queue.h"
 #include "checkpolicy.h"
@@ -652,6 +653,11 @@ int main(int argc, char **argv)
 		}
 	}
 
+	if (validate_policydb(NULL, policydbp)) {
+		fprintf(stderr, "%s:  validation of generated policy failed\n", argv[0]);
+		exit(1);
+	}
+
 	if (outfile) {
 		if (!strcmp(outfile, "-")) {
 			outfp = stdout;
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 39/40] [CROSS-PATCH] libsepol: avoid passing NULL pointer to memcpy
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (37 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 38/40] [WIP] checkpolicy: validate generated policies Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-05 15:45   ` [RFC PATCH v2 40/40] [CROSS-PATCH] libsepol: do not pass NULL " Christian Göttsche
                     ` (2 subsequent siblings)
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

memcpy(3) might be annotated with the function attribute nonnull and
UBSan then complains:

    module.c:296:3: runtime error: null pointer passed as argument 2, which is declared to never be null
        #0 0x7f2468efa5b3 in link_netfilter_contexts ./libsepol/src/module.c:296
        #1 0x7f2468efa5b3 in sepol_link_packages ./libsepol/src/module.c:337
        #2 0x562331e9e123 in main ./semodule-utils/semodule_link/semodule_link.c:145
        #3 0x7f2467e247ec in __libc_start_main ../csu/libc-start.c:332
        #4 0x562331e9d2a9 in _start (./destdir/usr/bin/semodule_link+0x32a9)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/module.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/libsepol/src/module.c b/libsepol/src/module.c
index 4a51f25c..549634b8 100644
--- a/libsepol/src/module.c
+++ b/libsepol/src/module.c
@@ -293,9 +293,11 @@ static int link_netfilter_contexts(sepol_module_package_t * base,
 	}
 	base->netfilter_contexts = base_context;
 	for (i = 0; i < num_modules; i++) {
-		memcpy(base->netfilter_contexts + base->netfilter_contexts_len,
-		       modules[i]->netfilter_contexts,
-		       modules[i]->netfilter_contexts_len);
+		if (modules[i]->netfilter_contexts_len > 0) {
+			memcpy(base->netfilter_contexts + base->netfilter_contexts_len,
+		           modules[i]->netfilter_contexts,
+		           modules[i]->netfilter_contexts_len);
+		}
 		base->netfilter_contexts_len +=
 		    modules[i]->netfilter_contexts_len;
 	}
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [RFC PATCH v2 40/40] [CROSS-PATCH] libsepol: do not pass NULL to memcpy
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (38 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 39/40] [CROSS-PATCH] libsepol: avoid passing NULL pointer to memcpy Christian Göttsche
@ 2021-11-05 15:45   ` Christian Göttsche
  2021-11-09 18:42   ` [RFC PATCH v2 00/36] libsepol: add fuzzer for reading binary policies James Carter
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
  41 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-11-05 15:45 UTC (permalink / raw)
  To: selinux

For the first iteration `mod->perm_map[sclassi]` is NULL, thus do not
use it as source of a memcpy(3), even with a size of 0.  memcpy(3) might
be annotated with the function attribute nonnull and UBSan then
complains:

    link.c:193:3: runtime error: null pointer passed as argument 2, which is declared to never be null

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   drop realloc rewrite, just check for 0 size

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/link.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libsepol/src/link.c b/libsepol/src/link.c
index bd986b7b..dfcb0673 100644
--- a/libsepol/src/link.c
+++ b/libsepol/src/link.c
@@ -191,8 +191,9 @@ static int permission_copy_callback(hashtab_key_t key, hashtab_datum_t datum,
 			ERR(state->handle, "Out of memory!");
 			return -1;
 		}
-		memcpy(newmap, mod->perm_map[sclassi],
-		       mod->perm_map_len[sclassi] * sizeof(*newmap));
+		if (mod->perm_map_len[sclassi] > 0) {
+			memcpy(newmap, mod->perm_map[sclassi], mod->perm_map_len[sclassi] * sizeof(*newmap));
+		}
 		free(mod->perm_map[sclassi]);
 		mod->perm_map[sclassi] = newmap;
 		mod->perm_map_len[sclassi] = perm->s.value;
-- 
2.33.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH v2 00/36] libsepol: add fuzzer for reading binary policies
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (39 preceding siblings ...)
  2021-11-05 15:45   ` [RFC PATCH v2 40/40] [CROSS-PATCH] libsepol: do not pass NULL " Christian Göttsche
@ 2021-11-09 18:42   ` James Carter
  2021-11-09 18:43     ` James Carter
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
  41 siblings, 1 reply; 135+ messages in thread
From: James Carter @ 2021-11-09 18:42 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Fri, Nov 5, 2021 at 12:11 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Add a libfuzz[1] based fuzzer testing the reading and parsing of binary policy
> files. This fuzzer will be run within the OSS-Fuzz service.
>
> Handle and reject a variety of edge cases causing crashes or resource leaks.
>
> The fifth patch ("libsepol/fuzz: limit element sizes for fuzzing") needs some
> discussion: To avoid oom reports from the fuzzer, caused by huge memory
> allocations, all identifiers are limited to a length of 2^16 for the fuzzer
> build only.  Probably there should be a limit for the release build too.
> Is there a specification for the binary policy format saying something about
> the maximum length of identifiers?
> After a quick look at the kernel sources (most interesting is str_read()) I
> could not find any limits either.
>
> [1]: https://llvm.org/docs/LibFuzzer.html
>
> v2:
>   - reorder patches
>     1. oss-fuzz related
>     2. libsepol parsing and other crashesand UB
>     3. enhance policy validation
>   - misc changes based on review by James Carter
>

I have comments on patch 02 and 30, everything else looks good. It was
a little hard to work through the reordering, but it does seem like
you addressed all my previous comments.

Thanks,
Jim


> Christian Göttsche (36):
>   cifuzz: enable report-unreproducible-crashes
>   cifuzz: use the default runtime of 600 seconds
>   libsepol/fuzz: silence secilc-fuzzer
>   libsepol: add libfuzz based fuzzer for reading binary policies
>   libsepol/fuzz: limit element sizes for fuzzing
>   libsepol: use logging framework in conditional.c
>   libsepol: use logging framework in ebitmap.c
>   libsepol: use mallocarray wrapper to avoid overflows
>   libsepol: use reallocarray wrapper to avoid overflows
>   libsepol: add checks for read sizes
>   libsepol: enforce avtab item limit
>   libsepol: clean memory on conditional insertion failure
>   libsepol: reject abnormal huge sid ids
>   libsepol: reject invalid filetrans source type
>   libsepol: zero member before potential dereference
>   libsepol: use size_t for indexes in strs helpers
>   libsepol: do not underflow on short format arguments
>   libsepol: do not crash on class gaps
>   libsepol: do not crash on user gaps
>   libsepol: use correct size for initial string list
>   libsepol: do not create a string list with initial size zero
>   libsepol: split validation of datum array gaps and entries
>   libsepol: validate MLS levels
>   libsepol: validate expanded user range and level
>   libsepol: validate permission count of classes
>   libsepol: resolve log message mismatch
>   libsepol: validate avtab and avrule types
>   libsepol: validate constraint expression operators and attributes
>   libsepol: validate type of avtab type rules
>   libsepol: validate ocontexts
>   libsepol: validate genfs contexts
>   libsepol: validate permissive types
>   libsepol: validate policy properties
>   libsepol: validate categories
>   libsepol: validate fsuse types
>   libsepol: validate class default targets
>
>  .github/workflows/cifuzz.yml     |   3 +-
>  libsepol/fuzz/binpolicy-fuzzer.c |  63 ++++
>  libsepol/fuzz/policy.bin         | Bin 0 -> 1552 bytes
>  libsepol/fuzz/secilc-fuzzer.c    |   5 +
>  libsepol/src/Makefile            |   6 +
>  libsepol/src/avtab.c             |   6 +
>  libsepol/src/conditional.c       |  53 ++--
>  libsepol/src/ebitmap.c           |  27 +-
>  libsepol/src/expand.c            |   4 +-
>  libsepol/src/hashtab.c           |   4 +-
>  libsepol/src/kernel_to_cil.c     |  10 +
>  libsepol/src/kernel_to_common.c  |  23 +-
>  libsepol/src/kernel_to_common.h  |   4 +-
>  libsepol/src/kernel_to_conf.c    |  13 +-
>  libsepol/src/link.c              |   3 +-
>  libsepol/src/module.c            |   4 +-
>  libsepol/src/module_to_cil.c     |  13 +-
>  libsepol/src/optimize.c          |  11 +-
>  libsepol/src/policydb.c          |  27 +-
>  libsepol/src/policydb_validate.c | 475 +++++++++++++++++++++++++++----
>  libsepol/src/private.h           |  27 +-
>  libsepol/src/services.c          |  12 +-
>  libsepol/src/sidtab.c            |   3 +-
>  libsepol/src/user_record.c       |   8 +-
>  libsepol/src/users.c             |  12 +-
>  libsepol/src/util.c              |  11 +-
>  libsepol/src/write.c             |   2 +-
>  scripts/oss-fuzz.sh              |  17 +-
>  28 files changed, 684 insertions(+), 162 deletions(-)
>  create mode 100644 libsepol/fuzz/binpolicy-fuzzer.c
>  create mode 100644 libsepol/fuzz/policy.bin
>
> --
> 2.33.1
>

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH v2 00/36] libsepol: add fuzzer for reading binary policies
  2021-11-09 18:42   ` [RFC PATCH v2 00/36] libsepol: add fuzzer for reading binary policies James Carter
@ 2021-11-09 18:43     ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-11-09 18:43 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Tue, Nov 9, 2021 at 1:42 PM James Carter <jwcart2@gmail.com> wrote:
>
> On Fri, Nov 5, 2021 at 12:11 PM Christian Göttsche
> <cgzones@googlemail.com> wrote:
> >
> > Add a libfuzz[1] based fuzzer testing the reading and parsing of binary policy
> > files. This fuzzer will be run within the OSS-Fuzz service.
> >
> > Handle and reject a variety of edge cases causing crashes or resource leaks.
> >
> > The fifth patch ("libsepol/fuzz: limit element sizes for fuzzing") needs some
> > discussion: To avoid oom reports from the fuzzer, caused by huge memory
> > allocations, all identifiers are limited to a length of 2^16 for the fuzzer
> > build only.  Probably there should be a limit for the release build too.
> > Is there a specification for the binary policy format saying something about
> > the maximum length of identifiers?
> > After a quick look at the kernel sources (most interesting is str_read()) I
> > could not find any limits either.
> >
> > [1]: https://llvm.org/docs/LibFuzzer.html
> >
> > v2:
> >   - reorder patches
> >     1. oss-fuzz related
> >     2. libsepol parsing and other crashesand UB
> >     3. enhance policy validation
> >   - misc changes based on review by James Carter
> >
>
> I have comments on patch 02 and 30, everything else looks good. It was

I meant patches 10 and 30.
Jim

> a little hard to work through the reordering, but it does seem like
> you addressed all my previous comments.
>
> Thanks,
> Jim
>
>
> > Christian Göttsche (36):
> >   cifuzz: enable report-unreproducible-crashes
> >   cifuzz: use the default runtime of 600 seconds
> >   libsepol/fuzz: silence secilc-fuzzer
> >   libsepol: add libfuzz based fuzzer for reading binary policies
> >   libsepol/fuzz: limit element sizes for fuzzing
> >   libsepol: use logging framework in conditional.c
> >   libsepol: use logging framework in ebitmap.c
> >   libsepol: use mallocarray wrapper to avoid overflows
> >   libsepol: use reallocarray wrapper to avoid overflows
> >   libsepol: add checks for read sizes
> >   libsepol: enforce avtab item limit
> >   libsepol: clean memory on conditional insertion failure
> >   libsepol: reject abnormal huge sid ids
> >   libsepol: reject invalid filetrans source type
> >   libsepol: zero member before potential dereference
> >   libsepol: use size_t for indexes in strs helpers
> >   libsepol: do not underflow on short format arguments
> >   libsepol: do not crash on class gaps
> >   libsepol: do not crash on user gaps
> >   libsepol: use correct size for initial string list
> >   libsepol: do not create a string list with initial size zero
> >   libsepol: split validation of datum array gaps and entries
> >   libsepol: validate MLS levels
> >   libsepol: validate expanded user range and level
> >   libsepol: validate permission count of classes
> >   libsepol: resolve log message mismatch
> >   libsepol: validate avtab and avrule types
> >   libsepol: validate constraint expression operators and attributes
> >   libsepol: validate type of avtab type rules
> >   libsepol: validate ocontexts
> >   libsepol: validate genfs contexts
> >   libsepol: validate permissive types
> >   libsepol: validate policy properties
> >   libsepol: validate categories
> >   libsepol: validate fsuse types
> >   libsepol: validate class default targets
> >
> >  .github/workflows/cifuzz.yml     |   3 +-
> >  libsepol/fuzz/binpolicy-fuzzer.c |  63 ++++
> >  libsepol/fuzz/policy.bin         | Bin 0 -> 1552 bytes
> >  libsepol/fuzz/secilc-fuzzer.c    |   5 +
> >  libsepol/src/Makefile            |   6 +
> >  libsepol/src/avtab.c             |   6 +
> >  libsepol/src/conditional.c       |  53 ++--
> >  libsepol/src/ebitmap.c           |  27 +-
> >  libsepol/src/expand.c            |   4 +-
> >  libsepol/src/hashtab.c           |   4 +-
> >  libsepol/src/kernel_to_cil.c     |  10 +
> >  libsepol/src/kernel_to_common.c  |  23 +-
> >  libsepol/src/kernel_to_common.h  |   4 +-
> >  libsepol/src/kernel_to_conf.c    |  13 +-
> >  libsepol/src/link.c              |   3 +-
> >  libsepol/src/module.c            |   4 +-
> >  libsepol/src/module_to_cil.c     |  13 +-
> >  libsepol/src/optimize.c          |  11 +-
> >  libsepol/src/policydb.c          |  27 +-
> >  libsepol/src/policydb_validate.c | 475 +++++++++++++++++++++++++++----
> >  libsepol/src/private.h           |  27 +-
> >  libsepol/src/services.c          |  12 +-
> >  libsepol/src/sidtab.c            |   3 +-
> >  libsepol/src/user_record.c       |   8 +-
> >  libsepol/src/users.c             |  12 +-
> >  libsepol/src/util.c              |  11 +-
> >  libsepol/src/write.c             |   2 +-
> >  scripts/oss-fuzz.sh              |  17 +-
> >  28 files changed, 684 insertions(+), 162 deletions(-)
> >  create mode 100644 libsepol/fuzz/binpolicy-fuzzer.c
> >  create mode 100644 libsepol/fuzz/policy.bin
> >
> > --
> > 2.33.1
> >

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH v2 10/36] libsepol: add checks for read sizes
  2021-11-05 15:45   ` [RFC PATCH v2 10/36] libsepol: add checks for read sizes Christian Göttsche
@ 2021-11-09 18:46     ` James Carter
  2021-11-09 18:58       ` Christian Göttsche
  0 siblings, 1 reply; 135+ messages in thread
From: James Carter @ 2021-11-09 18:46 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Fri, Nov 5, 2021 at 12:11 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Add checks for invalid read sizes from a binary policy to guard
> allocations.
>
> The common and class permission counts needs to be limited more strict
> otherwise a too high count of common or class permissions can lead to
> permission values with a too high value, which can lead to overflows
> in shift operations.
>
> In the fuzzer build the value will also be bounded to avoid oom reports.
>
>     ==29857== ERROR: libFuzzer: out-of-memory (malloc(17179868160))
>        To change the out-of-memory limit use -rss_limit_mb=<N>
>
>         #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
>         #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
>         #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
>         #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
>         #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
>         #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
>         #6 0x4aa143 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa143)
>         #7 0x5259cb in malloc (./out/binpolicy-fuzzer+0x5259cb)
>         #8 0x580b5d in mallocarray ./libsepol/src/./private.h:93:9
>         #9 0x57c2ed in scope_read ./libsepol/src/policydb.c:4120:7
>         #10 0x576b0d in policydb_read ./libsepol/src/policydb.c:4462:9
>         #11 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
>         #12 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
>         #13 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
>         #14 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
>         #15 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
>         #16 0x7ffad6e107ec in __libc_start_main csu/../csu/libc-start.c:332:16
>         #17 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
>
>     ==19462== ERROR: libFuzzer: out-of-memory (malloc(18253611008))
>        To change the out-of-memory limit use -rss_limit_mb=<N>
>
>         #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
>         #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
>         #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
>         #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
>         #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
>         #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
>         #6 0x4aa999 in __asan::asan_calloc(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa999)
>         #7 0x525b63 in __interceptor_calloc (./out/binpolicy-fuzzer+0x525b63)
>         #8 0x570938 in policydb_index_others ./libsepol/src/policydb.c:1245:6
>         #9 0x5771f3 in policydb_read ./src/policydb.c:4481:6
>         #10 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
>         #11 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
>         #12 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
>         #13 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
>         #14 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
>         #15 0x7f4d933157ec in __libc_start_main csu/../csu/libc-start.c:332:16
>         #16 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/src/policydb.c | 10 +++++++++-
>  1 file changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
> index dcea1807..1408405d 100644
> --- a/libsepol/src/policydb.c
> +++ b/libsepol/src/policydb.c
> @@ -2103,6 +2103,8 @@ static int common_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
>         if (symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE))
>                 goto bad;
>         comdatum->permissions.nprim = le32_to_cpu(buf[2]);
> +       if (comdatum->permissions.nprim > 32)

Should use PERM_SYMTAB_SIZE here (like in patch 22).

> +               goto bad;
>         nel = le32_to_cpu(buf[3]);
>
>         key = malloc(len + 1);
> @@ -2251,6 +2253,8 @@ static int class_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
>         if (symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE))
>                 goto bad;
>         cladatum->permissions.nprim = le32_to_cpu(buf[3]);
> +       if (cladatum->permissions.nprim > 32)

Same here.


> +               goto bad;
>         nel = le32_to_cpu(buf[4]);
>
>         ncons = le32_to_cpu(buf[5]);
> @@ -3980,6 +3984,8 @@ static int avrule_decl_read(policydb_t * p, avrule_decl_t * decl,
>                 if (rc < 0)
>                         return -1;
>                 nprim = le32_to_cpu(buf[0]);
> +               if (is_saturated(nprim))
> +                       return -1;
>                 nel = le32_to_cpu(buf[1]);
>                 for (j = 0; j < nel; j++) {
>                         if (read_f[i] (p, decl->symtab[i].table, fp)) {
> @@ -4106,7 +4112,7 @@ static int scope_read(policydb_t * p, int symnum, struct policy_file *fp)
>                 goto cleanup;
>         scope->scope = le32_to_cpu(buf[0]);
>         scope->decl_ids_len = le32_to_cpu(buf[1]);
> -       if (scope->decl_ids_len == 0) {
> +       if (zero_or_saturated(scope->decl_ids_len)) {
>                 ERR(fp->handle, "invalid scope with no declaration");
>                 goto cleanup;
>         }
> @@ -4396,6 +4402,8 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose)
>                 if (rc < 0)
>                         goto bad;
>                 nprim = le32_to_cpu(buf[0]);
> +               if (is_saturated(nprim))
> +                       goto bad;
>                 nel = le32_to_cpu(buf[1]);
>                 if (nel && !nprim) {
>                         ERR(fp->handle, "unexpected items in symbol table with no symbol");
> --
> 2.33.1
>

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH v2 10/36] libsepol: add checks for read sizes
  2021-11-09 18:46     ` James Carter
@ 2021-11-09 18:58       ` Christian Göttsche
  2021-11-09 19:17         ` James Carter
  0 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-11-09 18:58 UTC (permalink / raw)
  To: James Carter; +Cc: SElinux list

On Tue, 9 Nov 2021 at 19:46, James Carter <jwcart2@gmail.com> wrote:
>
> On Fri, Nov 5, 2021 at 12:11 PM Christian Göttsche
> <cgzones@googlemail.com> wrote:
> >
> > Add checks for invalid read sizes from a binary policy to guard
> > allocations.
> >
> > The common and class permission counts needs to be limited more strict
> > otherwise a too high count of common or class permissions can lead to
> > permission values with a too high value, which can lead to overflows
> > in shift operations.
> >
> > In the fuzzer build the value will also be bounded to avoid oom reports.
> >
> >     ==29857== ERROR: libFuzzer: out-of-memory (malloc(17179868160))
> >        To change the out-of-memory limit use -rss_limit_mb=<N>
> >
> >         #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
> >         #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
> >         #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
> >         #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
> >         #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
> >         #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
> >         #6 0x4aa143 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa143)
> >         #7 0x5259cb in malloc (./out/binpolicy-fuzzer+0x5259cb)
> >         #8 0x580b5d in mallocarray ./libsepol/src/./private.h:93:9
> >         #9 0x57c2ed in scope_read ./libsepol/src/policydb.c:4120:7
> >         #10 0x576b0d in policydb_read ./libsepol/src/policydb.c:4462:9
> >         #11 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
> >         #12 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
> >         #13 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
> >         #14 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
> >         #15 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
> >         #16 0x7ffad6e107ec in __libc_start_main csu/../csu/libc-start.c:332:16
> >         #17 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
> >
> >     ==19462== ERROR: libFuzzer: out-of-memory (malloc(18253611008))
> >        To change the out-of-memory limit use -rss_limit_mb=<N>
> >
> >         #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
> >         #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
> >         #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
> >         #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
> >         #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
> >         #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
> >         #6 0x4aa999 in __asan::asan_calloc(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa999)
> >         #7 0x525b63 in __interceptor_calloc (./out/binpolicy-fuzzer+0x525b63)
> >         #8 0x570938 in policydb_index_others ./libsepol/src/policydb.c:1245:6
> >         #9 0x5771f3 in policydb_read ./src/policydb.c:4481:6
> >         #10 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
> >         #11 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
> >         #12 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
> >         #13 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
> >         #14 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
> >         #15 0x7f4d933157ec in __libc_start_main csu/../csu/libc-start.c:332:16
> >         #16 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
> >
> > Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> > ---
> >  libsepol/src/policydb.c | 10 +++++++++-
> >  1 file changed, 9 insertions(+), 1 deletion(-)
> >
> > diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
> > index dcea1807..1408405d 100644
> > --- a/libsepol/src/policydb.c
> > +++ b/libsepol/src/policydb.c
> > @@ -2103,6 +2103,8 @@ static int common_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
> >         if (symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE))
> >                 goto bad;
> >         comdatum->permissions.nprim = le32_to_cpu(buf[2]);
> > +       if (comdatum->permissions.nprim > 32)
>
> Should use PERM_SYMTAB_SIZE here (like in patch 22).

I thought about that, but it seemed a bit unrelated.
In patch 25 (https://patchwork.kernel.org/project/selinux/patch/20211105154542.38434-26-cgzones@googlemail.com/)
the number of permissions in commons and classes is checked against
the logical limit (which is imposed by the implementation via a bitset
on a 32 bit integer).
The check here is to prevent overflows on subsequent left-shifts by
the checked value on a 32 bit integer.
Maybe `sizeof(uint32_t)` is more expressive?
However I do not object your comment, so I fine to change to PERM_SYMTAB_SIZE.

> > +               goto bad;
> >         nel = le32_to_cpu(buf[3]);
> >
> >         key = malloc(len + 1);
> > @@ -2251,6 +2253,8 @@ static int class_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
> >         if (symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE))
> >                 goto bad;
> >         cladatum->permissions.nprim = le32_to_cpu(buf[3]);
> > +       if (cladatum->permissions.nprim > 32)
>
> Same here.
>
>
> > +               goto bad;
> >         nel = le32_to_cpu(buf[4]);
> >
> >         ncons = le32_to_cpu(buf[5]);
> > @@ -3980,6 +3984,8 @@ static int avrule_decl_read(policydb_t * p, avrule_decl_t * decl,
> >                 if (rc < 0)
> >                         return -1;
> >                 nprim = le32_to_cpu(buf[0]);
> > +               if (is_saturated(nprim))
> > +                       return -1;
> >                 nel = le32_to_cpu(buf[1]);
> >                 for (j = 0; j < nel; j++) {
> >                         if (read_f[i] (p, decl->symtab[i].table, fp)) {
> > @@ -4106,7 +4112,7 @@ static int scope_read(policydb_t * p, int symnum, struct policy_file *fp)
> >                 goto cleanup;
> >         scope->scope = le32_to_cpu(buf[0]);
> >         scope->decl_ids_len = le32_to_cpu(buf[1]);
> > -       if (scope->decl_ids_len == 0) {
> > +       if (zero_or_saturated(scope->decl_ids_len)) {
> >                 ERR(fp->handle, "invalid scope with no declaration");
> >                 goto cleanup;
> >         }
> > @@ -4396,6 +4402,8 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose)
> >                 if (rc < 0)
> >                         goto bad;
> >                 nprim = le32_to_cpu(buf[0]);
> > +               if (is_saturated(nprim))
> > +                       goto bad;
> >                 nel = le32_to_cpu(buf[1]);
> >                 if (nel && !nprim) {
> >                         ERR(fp->handle, "unexpected items in symbol table with no symbol");
> > --
> > 2.33.1
> >

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH v2 30/36] libsepol: validate ocontexts
  2021-11-05 15:45   ` [RFC PATCH v2 30/36] libsepol: validate ocontexts Christian Göttsche
@ 2021-11-09 19:04     ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-11-09 19:04 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Fri, Nov 5, 2021 at 12:11 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Check the literal contexts in ocontext statements are defined.
>
>     ==91274==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f60b0afe8c6 bp 0x7ffd42edc990 sp 0x7ffd42edc148 T0)
>     ==91274==The signal is caused by a READ memory access.
>     ==91274==Hint: address points to the zero page.
>         #0 0x7f60b0afe8c6  string/../sysdeps/x86_64/multiarch/../strlen.S:120
>         #1 0x4bd128 in __interceptor_strlen (./out/binpolicy-fuzzer+0x4bd128)
>         #2 0x5eb387 in create_str_helper ./libsepol/src/kernel_to_common.c:69:10
>         #3 0x5eb11e in create_str ./libsepol/src/kernel_to_common.c:99:8
>         #4 0x56ad7b in context_to_str ./libsepol/src/kernel_to_conf.c:2408:9
>         #5 0x56a717 in write_sid_context_rules_to_conf ./libsepol/src/kernel_to_conf.c:2441:9
>         #6 0x55b26c in write_selinux_isid_rules_to_conf ./libsepol/src/kernel_to_conf.c:2476:9
>         #7 0x55b26c in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3206:8
>         #8 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
>         #9 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
>         #10 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
>         #11 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
>         #12 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
>         #13 0x7f60b0a887ec in __libc_start_main csu/../csu/libc-start.c:332:16
>         #14 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
>
> ---
> v2:
>    also check in base modules
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
>  libsepol/src/policydb_validate.c | 44 ++++++++++++++++++++++++++++++++
>  1 file changed, 44 insertions(+)
>
> diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
> index 57eb2550..96f133c9 100644
> --- a/libsepol/src/policydb_validate.c
> +++ b/libsepol/src/policydb_validate.c
> @@ -736,6 +736,47 @@ static int validate_filename_trans_hashtab(sepol_handle_t *handle, hashtab_t fil
>         return 0;
>  }
>
> +static int validate_context(context_struct_t *con, validate_t flavors[], int mls)
> +{
> +       if (validate_value(con->user, &flavors[SYM_USERS]))
> +               return -1;
> +       if (validate_value(con->role, &flavors[SYM_ROLES]))
> +               return -1;
> +       if (validate_value(con->type, &flavors[SYM_TYPES]))
> +               return -1;
> +       if (mls && validate_mls_range(&con->range, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
> +               return -1;
> +
> +       return 0;
> +}
> +
> +static int validate_ocontexts(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
> +{
> +       ocontext_t *octx;
> +       unsigned int i;
> +
> +       for (i = 0; i < OCON_NUM; i++) {
> +               for (octx = p->ocontexts[i]; octx; octx = octx->next) {
> +                       if (validate_context(&octx->context[0], flavors, p->mls))
> +                               goto bad;
> +
> +                       switch (i) {
> +                       case OCON_FS:
> +                       case OCON_NETIF:
> +                               if (validate_context(&octx->context[1], flavors, p->mls))
> +                                       goto bad;
> +                               break;

Should use a default case.

The bigger issue, which I didn't catch the first time, is that this
doesn't work for a XEN policy.
In policydb.c there is an ocontext_read_xen() and an
ocontext_read_selinux(), which is called depends on whether
p->target_platform is SEPOL_TARGET_SELINUX or SEPOL_TARGET_XEN.

Jim


> +                       }
> +               }
> +       }
> +
> +       return 0;
> +
> +bad:
> +       ERR(handle, "Invalid ocontext");
> +       return -1;
> +}
> +
>  /*
>   * Functions to validate a module policydb
>   */
> @@ -936,6 +977,9 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
>                         goto bad;
>         }
>
> +       if (validate_ocontexts(handle, p, flavors))
> +               goto bad;
> +
>         if (validate_scopes(handle, p->scope, p->global))
>                 goto bad;
>
> --
> 2.33.1
>

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [RFC PATCH v2 10/36] libsepol: add checks for read sizes
  2021-11-09 18:58       ` Christian Göttsche
@ 2021-11-09 19:17         ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-11-09 19:17 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Tue, Nov 9, 2021 at 1:59 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> On Tue, 9 Nov 2021 at 19:46, James Carter <jwcart2@gmail.com> wrote:
> >
> > On Fri, Nov 5, 2021 at 12:11 PM Christian Göttsche
> > <cgzones@googlemail.com> wrote:
> > >
> > > Add checks for invalid read sizes from a binary policy to guard
> > > allocations.
> > >
> > > The common and class permission counts needs to be limited more strict
> > > otherwise a too high count of common or class permissions can lead to
> > > permission values with a too high value, which can lead to overflows
> > > in shift operations.
> > >
> > > In the fuzzer build the value will also be bounded to avoid oom reports.
> > >
> > >     ==29857== ERROR: libFuzzer: out-of-memory (malloc(17179868160))
> > >        To change the out-of-memory limit use -rss_limit_mb=<N>
> > >
> > >         #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
> > >         #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
> > >         #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
> > >         #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
> > >         #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
> > >         #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
> > >         #6 0x4aa143 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa143)
> > >         #7 0x5259cb in malloc (./out/binpolicy-fuzzer+0x5259cb)
> > >         #8 0x580b5d in mallocarray ./libsepol/src/./private.h:93:9
> > >         #9 0x57c2ed in scope_read ./libsepol/src/policydb.c:4120:7
> > >         #10 0x576b0d in policydb_read ./libsepol/src/policydb.c:4462:9
> > >         #11 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
> > >         #12 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
> > >         #13 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
> > >         #14 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
> > >         #15 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
> > >         #16 0x7ffad6e107ec in __libc_start_main csu/../csu/libc-start.c:332:16
> > >         #17 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
> > >
> > >     ==19462== ERROR: libFuzzer: out-of-memory (malloc(18253611008))
> > >        To change the out-of-memory limit use -rss_limit_mb=<N>
> > >
> > >         #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
> > >         #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
> > >         #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
> > >         #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
> > >         #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
> > >         #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
> > >         #6 0x4aa999 in __asan::asan_calloc(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa999)
> > >         #7 0x525b63 in __interceptor_calloc (./out/binpolicy-fuzzer+0x525b63)
> > >         #8 0x570938 in policydb_index_others ./libsepol/src/policydb.c:1245:6
> > >         #9 0x5771f3 in policydb_read ./src/policydb.c:4481:6
> > >         #10 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
> > >         #11 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
> > >         #12 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
> > >         #13 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
> > >         #14 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
> > >         #15 0x7f4d933157ec in __libc_start_main csu/../csu/libc-start.c:332:16
> > >         #16 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
> > >
> > > Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> > > ---
> > >  libsepol/src/policydb.c | 10 +++++++++-
> > >  1 file changed, 9 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
> > > index dcea1807..1408405d 100644
> > > --- a/libsepol/src/policydb.c
> > > +++ b/libsepol/src/policydb.c
> > > @@ -2103,6 +2103,8 @@ static int common_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
> > >         if (symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE))
> > >                 goto bad;
> > >         comdatum->permissions.nprim = le32_to_cpu(buf[2]);
> > > +       if (comdatum->permissions.nprim > 32)
> >
> > Should use PERM_SYMTAB_SIZE here (like in patch 22).
>
> I thought about that, but it seemed a bit unrelated.
> In patch 25 (https://patchwork.kernel.org/project/selinux/patch/20211105154542.38434-26-cgzones@googlemail.com/)
> the number of permissions in commons and classes is checked against
> the logical limit (which is imposed by the implementation via a bitset
> on a 32 bit integer).
> The check here is to prevent overflows on subsequent left-shifts by
> the checked value on a 32 bit integer.
> Maybe `sizeof(uint32_t)` is more expressive?
> However I do not object your comment, so I fine to change to PERM_SYMTAB_SIZE.
>

I think that you make a good point, but I think that PERM_SYMTAB_SIZE,
while not perfect, is the best choice.

Thanks,
Jim


> > > +               goto bad;
> > >         nel = le32_to_cpu(buf[3]);
> > >
> > >         key = malloc(len + 1);
> > > @@ -2251,6 +2253,8 @@ static int class_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
> > >         if (symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE))
> > >                 goto bad;
> > >         cladatum->permissions.nprim = le32_to_cpu(buf[3]);
> > > +       if (cladatum->permissions.nprim > 32)
> >
> > Same here.
> >
> >
> > > +               goto bad;
> > >         nel = le32_to_cpu(buf[4]);
> > >
> > >         ncons = le32_to_cpu(buf[5]);
> > > @@ -3980,6 +3984,8 @@ static int avrule_decl_read(policydb_t * p, avrule_decl_t * decl,
> > >                 if (rc < 0)
> > >                         return -1;
> > >                 nprim = le32_to_cpu(buf[0]);
> > > +               if (is_saturated(nprim))
> > > +                       return -1;
> > >                 nel = le32_to_cpu(buf[1]);
> > >                 for (j = 0; j < nel; j++) {
> > >                         if (read_f[i] (p, decl->symtab[i].table, fp)) {
> > > @@ -4106,7 +4112,7 @@ static int scope_read(policydb_t * p, int symnum, struct policy_file *fp)
> > >                 goto cleanup;
> > >         scope->scope = le32_to_cpu(buf[0]);
> > >         scope->decl_ids_len = le32_to_cpu(buf[1]);
> > > -       if (scope->decl_ids_len == 0) {
> > > +       if (zero_or_saturated(scope->decl_ids_len)) {
> > >                 ERR(fp->handle, "invalid scope with no declaration");
> > >                 goto cleanup;
> > >         }
> > > @@ -4396,6 +4402,8 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose)
> > >                 if (rc < 0)
> > >                         goto bad;
> > >                 nprim = le32_to_cpu(buf[0]);
> > > +               if (is_saturated(nprim))
> > > +                       goto bad;
> > >                 nel = le32_to_cpu(buf[1]);
> > >                 if (nel && !nprim) {
> > >                         ERR(fp->handle, "unexpected items in symbol table with no symbol");
> > > --
> > > 2.33.1
> > >

^ permalink raw reply	[flat|nested] 135+ messages in thread

* [PATCH v3 00/36] libsepol: add fuzzer for reading binary policies
  2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
                     ` (40 preceding siblings ...)
  2021-11-09 18:42   ` [RFC PATCH v2 00/36] libsepol: add fuzzer for reading binary policies James Carter
@ 2021-12-09 16:48   ` Christian Göttsche
  2021-12-09 16:48     ` [PATCH v3 01/36] cifuzz: enable report-unreproducible-crashes Christian Göttsche
                       ` (36 more replies)
  41 siblings, 37 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:48 UTC (permalink / raw)
  To: selinux

Add a libfuzz[1] based fuzzer testing the reading and parsing of binary policy
files. This fuzzer will be run within the OSS-Fuzz service.

Handle and reject a variety of edge cases causing crashes or resource leaks.

The fifth patch ("libsepol/fuzz: limit element sizes for fuzzing") limits, to
avoid oom reports from the fuzzer, caused by huge memory allocations, all
identifiers to a length of 2^16 for the fuzzer build only.
A potential limit for the release build needs further discussion.

[1]: https://llvm.org/docs/LibFuzzer.html

v3:
  - Drop RFC status
  - [10] libsepol: add checks for read sizes
    use PERM_SYMTAB_SIZE instead of bare 32 as limit
  - [11] libsepol: enforce avtab item limit
    take zero based numbering of variable items into account
  - [30] libsepol: validate ocontexts
    only check FS and NETIF ocons in selinux policies (not xen)

v2:
  - reorder patches
    1. oss-fuzz related
    2. libsepol parsing and other crashesand UB
    3. enhance policy validation
  - misc changes based on review by James Carter


Christian Göttsche (36):
  cifuzz: enable report-unreproducible-crashes
  cifuzz: use the default runtime of 600 seconds
  libsepol/fuzz: silence secilc-fuzzer
  libsepol: add libfuzz based fuzzer for reading binary policies
  libsepol/fuzz: limit element sizes for fuzzing
  libsepol: use logging framework in conditional.c
  libsepol: use logging framework in ebitmap.c
  libsepol: use mallocarray wrapper to avoid overflows
  libsepol: use reallocarray wrapper to avoid overflows
  libsepol: add checks for read sizes
  libsepol: enforce avtab item limit
  libsepol: clean memory on conditional insertion failure
  libsepol: reject abnormal huge sid ids
  libsepol: reject invalid filetrans source type
  libsepol: zero member before potential dereference
  libsepol: use size_t for indexes in strs helpers
  libsepol: do not underflow on short format arguments
  libsepol: do not crash on class gaps
  libsepol: do not crash on user gaps
  libsepol: use correct size for initial string list
  libsepol: do not create a string list with initial size zero
  libsepol: split validation of datum array gaps and entries
  libsepol: validate MLS levels
  libsepol: validate expanded user range and level
  libsepol: validate permission count of classes
  libsepol: resolve log message mismatch
  libsepol: validate avtab and avrule types
  libsepol: validate constraint expression operators and attributes
  libsepol: validate type of avtab type rules
  libsepol: validate ocontexts
  libsepol: validate genfs contexts
  libsepol: validate permissive types
  libsepol: validate policy properties
  libsepol: validate categories
  libsepol: validate fsuse types
  libsepol: validate class default targets

 .github/workflows/cifuzz.yml     |   3 +-
 libsepol/fuzz/binpolicy-fuzzer.c |  63 ++++
 libsepol/fuzz/policy.bin         | Bin 0 -> 1552 bytes
 libsepol/fuzz/secilc-fuzzer.c    |   5 +
 libsepol/src/Makefile            |   6 +
 libsepol/src/avtab.c             |   6 +
 libsepol/src/conditional.c       |  53 ++--
 libsepol/src/ebitmap.c           |  27 +-
 libsepol/src/expand.c            |   4 +-
 libsepol/src/hashtab.c           |   4 +-
 libsepol/src/kernel_to_cil.c     |  10 +
 libsepol/src/kernel_to_common.c  |  23 +-
 libsepol/src/kernel_to_common.h  |   4 +-
 libsepol/src/kernel_to_conf.c    |  13 +-
 libsepol/src/link.c              |   3 +-
 libsepol/src/module.c            |   4 +-
 libsepol/src/module_to_cil.c     |  13 +-
 libsepol/src/optimize.c          |  11 +-
 libsepol/src/policydb.c          |  27 +-
 libsepol/src/policydb_validate.c | 477 +++++++++++++++++++++++++++----
 libsepol/src/private.h           |  27 +-
 libsepol/src/services.c          |  12 +-
 libsepol/src/sidtab.c            |   3 +-
 libsepol/src/user_record.c       |   8 +-
 libsepol/src/users.c             |  12 +-
 libsepol/src/util.c              |  11 +-
 libsepol/src/write.c             |   2 +-
 scripts/oss-fuzz.sh              |  17 +-
 28 files changed, 686 insertions(+), 162 deletions(-)
 create mode 100644 libsepol/fuzz/binpolicy-fuzzer.c
 create mode 100644 libsepol/fuzz/policy.bin

-- 
2.34.1


^ permalink raw reply	[flat|nested] 135+ messages in thread

* [PATCH v3 01/36] cifuzz: enable report-unreproducible-crashes
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
@ 2021-12-09 16:48     ` Christian Göttsche
  2021-12-09 16:48     ` [PATCH v3 02/36] cifuzz: use the default runtime of 600 seconds Christian Göttsche
                       ` (35 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:48 UTC (permalink / raw)
  To: selinux

Fail and report unreproducible fuzzing crashes and leaks. Such failures
are probably related to some global state not properly reset in the
fuzzer and can cause OSS-Fuzz to report flaky issues.

Suggested-by: Evgeny Vereshchagin
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 .github/workflows/cifuzz.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
index 5c2233a2..b28eb71a 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/cifuzz.yml
@@ -30,6 +30,7 @@ jobs:
           oss-fuzz-project-name: 'selinux'
           fuzz-seconds: 180
           dry-run: false
+          report-unreproducible-crashes: true
           sanitizer: ${{ matrix.sanitizer }}
       - name: Upload Crash
         uses: actions/upload-artifact@v1
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 02/36] cifuzz: use the default runtime of 600 seconds
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
  2021-12-09 16:48     ` [PATCH v3 01/36] cifuzz: enable report-unreproducible-crashes Christian Göttsche
@ 2021-12-09 16:48     ` Christian Göttsche
  2021-12-09 16:48     ` [PATCH v3 03/36] libsepol/fuzz: silence secilc-fuzzer Christian Göttsche
                       ` (34 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:48 UTC (permalink / raw)
  To: selinux

The default runtime for CIFuzz[1] is 600 seconds; use it.

Since GitHub pull-requests are not the main contribution workflow the
number of runs should be manageable.

[1]: https://google.github.io/oss-fuzz/getting-started/continuous-integration/

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 .github/workflows/cifuzz.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
index b28eb71a..92523db4 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/cifuzz.yml
@@ -28,7 +28,7 @@ jobs:
         uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
         with:
           oss-fuzz-project-name: 'selinux'
-          fuzz-seconds: 180
+          fuzz-seconds: 600
           dry-run: false
           report-unreproducible-crashes: true
           sanitizer: ${{ matrix.sanitizer }}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 03/36] libsepol/fuzz: silence secilc-fuzzer
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
  2021-12-09 16:48     ` [PATCH v3 01/36] cifuzz: enable report-unreproducible-crashes Christian Göttsche
  2021-12-09 16:48     ` [PATCH v3 02/36] cifuzz: use the default runtime of 600 seconds Christian Göttsche
@ 2021-12-09 16:48     ` Christian Göttsche
  2021-12-09 16:48     ` [PATCH v3 04/36] libsepol: add libfuzz based fuzzer for reading binary policies Christian Göttsche
                       ` (33 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:48 UTC (permalink / raw)
  To: selinux

Do not output CIL log messages while fuzzing, since their amount are
huge, e.g. for neverallow or typebounds violations.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/fuzz/secilc-fuzzer.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libsepol/fuzz/secilc-fuzzer.c b/libsepol/fuzz/secilc-fuzzer.c
index 255b3241..9a1a16de 100644
--- a/libsepol/fuzz/secilc-fuzzer.c
+++ b/libsepol/fuzz/secilc-fuzzer.c
@@ -8,6 +8,10 @@
 #include <sepol/cil/cil.h>
 #include <sepol/policydb.h>
 
+static void log_handler(__attribute__((unused)) int lvl, __attribute__((unused)) const char *msg) {
+	/* be quiet */
+}
+
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
 	enum cil_log_level log_level = CIL_ERR;
 	struct sepol_policy_file *pf = NULL;
@@ -24,6 +28,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
 	sepol_policydb_t *pdb = NULL;
 
 	cil_set_log_level(log_level);
+	cil_set_log_handler(log_handler);
 
 	cil_db_init(&db);
 	cil_set_disable_dontaudit(db, disable_dontaudit);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 04/36] libsepol: add libfuzz based fuzzer for reading binary policies
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (2 preceding siblings ...)
  2021-12-09 16:48     ` [PATCH v3 03/36] libsepol/fuzz: silence secilc-fuzzer Christian Göttsche
@ 2021-12-09 16:48     ` Christian Göttsche
  2021-12-09 16:48     ` [PATCH v3 05/36] libsepol/fuzz: limit element sizes for fuzzing Christian Göttsche
                       ` (32 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:48 UTC (permalink / raw)
  To: selinux

Introduce a libfuzz[1] based fuzzer testing the parsing of a binary
policy.

Build the fuzzer in the oss-fuzz script.

[1]: https://llvm.org/docs/LibFuzzer.html

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/fuzz/binpolicy-fuzzer.c |  63 +++++++++++++++++++++++++++++++
 libsepol/fuzz/policy.bin         | Bin 0 -> 1552 bytes
 scripts/oss-fuzz.sh              |  17 ++++++++-
 3 files changed, 78 insertions(+), 2 deletions(-)
 create mode 100644 libsepol/fuzz/binpolicy-fuzzer.c
 create mode 100644 libsepol/fuzz/policy.bin

diff --git a/libsepol/fuzz/binpolicy-fuzzer.c b/libsepol/fuzz/binpolicy-fuzzer.c
new file mode 100644
index 00000000..85c59645
--- /dev/null
+++ b/libsepol/fuzz/binpolicy-fuzzer.c
@@ -0,0 +1,63 @@
+#include <sepol/debug.h>
+#include <sepol/kernel_to_cil.h>
+#include <sepol/kernel_to_conf.h>
+#include <sepol/policydb/policydb.h>
+
+extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static int write_binary_policy(policydb_t *p, FILE *outfp)
+{
+	struct policy_file pf;
+
+	policy_file_init(&pf);
+	pf.type = PF_USE_STDIO;
+	pf.fp = outfp;
+	return policydb_write(p, &pf);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+	policydb_t policydb = {};
+	sidtab_t sidtab = {};
+	struct policy_file pf;
+	FILE *devnull = NULL;
+
+	sepol_debug(0);
+
+	policy_file_init(&pf);
+	pf.type = PF_USE_MEMORY;
+	pf.data = (char *) data;
+	pf.len = size;
+
+	if (policydb_init(&policydb))
+		goto exit;
+
+	if (policydb_read(&policydb, &pf, /*verbose=*/0))
+		goto exit;
+
+	if (policydb_load_isids(&policydb, &sidtab))
+		goto exit;
+
+	if (policydb.policy_type == POLICY_KERN)
+		(void) policydb_optimize(&policydb);
+
+	devnull = fopen("/dev/null", "w");
+	if (!devnull)
+		goto exit;
+
+	(void) write_binary_policy(&policydb, devnull);
+
+	(void) sepol_kernel_policydb_to_conf(devnull, &policydb);
+
+	(void) sepol_kernel_policydb_to_cil(devnull, &policydb);
+
+exit:
+	if (devnull != NULL)
+		fclose(devnull);
+
+	policydb_destroy(&policydb);
+	sepol_sidtab_destroy(&sidtab);
+
+	/* Non-zero return values are reserved for future use. */
+	return 0;
+}
diff --git a/libsepol/fuzz/policy.bin b/libsepol/fuzz/policy.bin
new file mode 100644
index 0000000000000000000000000000000000000000..6f977ef34479daa9bf2e848c502ecea8d96f7912
GIT binary patch
literal 1552
zcma)5OLBuS3?==4PtZ+{&?9)$U3WbIlYnX65X0D})6Db;y>M5p9{5ov4Nx%;$<mW$
z3H<r}@pX|T$<xE~(b(pFDfU7D-=#naD2m2Fg9jW(-^m~bGdGSNY)e53<fv2qdtGkQ
z!jzhhLpdx(PWIwvbIwVQy0qhU$VF|O4}e{}D%0NI#$~><!L6(}!BqAt@_s$c!a#sw
zC$j7XLx!Aos(%-zs7Bl3l+Sv4XN--GML2e*`6?Tq1A9jjY>40a)K#TcVgu}o@qItz
z*n@Vpe$`n>9k>)lLo|5!#vC+5dA)f~edbIZ(r^=r47z&T$5@O7acJXBjy1qIauI91
zZV#hm%^Wra%{;^@N(_Mfp@x57&=A0V{XH^N#4uZ2$+ZB!To<dR3|?FRA3At3Wr~g%
zz~016vi3X+@#ERQVqoAOx)TgDxswt<g<podAL6jTDGsjGTrHewj)RLe$3eey9G;aL
td_V~(^i6Tdr3M$kUC#Ae9okPlwF6@KhlL%sbur5q>K{?!0dQgn^$*KOS$hBg

literal 0
HcmV?d00001

diff --git a/scripts/oss-fuzz.sh b/scripts/oss-fuzz.sh
index 16cc3c0a..72d275e8 100755
--- a/scripts/oss-fuzz.sh
+++ b/scripts/oss-fuzz.sh
@@ -32,7 +32,7 @@ SANITIZER=${SANITIZER:-address}
 flags="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=$SANITIZER -fsanitize=fuzzer-no-link"
 
 export CC=${CC:-clang}
-export CFLAGS=${CFLAGS:-$flags}
+export CFLAGS="${CFLAGS:-$flags} -I$DESTDIR/usr/include -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
 
 export CXX=${CXX:-clang++}
 export CXXFLAGS=${CXXFLAGS:-$flags}
@@ -49,11 +49,24 @@ make -C libsepol clean
 # shellcheck disable=SC2016
 make -C libsepol V=1 LD_SONAME_FLAGS='-soname,$(LIBSO),--version-script=$(LIBMAP)' -j"$(nproc)" install
 
+## secilc fuzzer ##
+
 # CFLAGS, CXXFLAGS and LIB_FUZZING_ENGINE have to be split to be accepted by
 # the compiler/linker so they shouldn't be quoted
 # shellcheck disable=SC2086
-$CC $CFLAGS -I"$DESTDIR/usr/include" -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -c -o secilc-fuzzer.o libsepol/fuzz/secilc-fuzzer.c
+$CC $CFLAGS -c -o secilc-fuzzer.o libsepol/fuzz/secilc-fuzzer.c
 # shellcheck disable=SC2086
 $CXX $CXXFLAGS $LIB_FUZZING_ENGINE secilc-fuzzer.o "$DESTDIR/usr/lib/libsepol.a" -o "$OUT/secilc-fuzzer"
 
 zip -r "$OUT/secilc-fuzzer_seed_corpus.zip" secilc/test
+
+## binary policy fuzzer ##
+
+# CFLAGS, CXXFLAGS and LIB_FUZZING_ENGINE have to be split to be accepted by
+# the compiler/linker so they shouldn't be quoted
+# shellcheck disable=SC2086
+$CC $CFLAGS -c -o binpolicy-fuzzer.o libsepol/fuzz/binpolicy-fuzzer.c
+# shellcheck disable=SC2086
+$CXX $CXXFLAGS $LIB_FUZZING_ENGINE binpolicy-fuzzer.o "$DESTDIR/usr/lib/libsepol.a" -o "$OUT/binpolicy-fuzzer"
+
+zip -j "$OUT/binpolicy-fuzzer_seed_corpus.zip" libsepol/fuzz/policy.bin
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 05/36] libsepol/fuzz: limit element sizes for fuzzing
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (3 preceding siblings ...)
  2021-12-09 16:48     ` [PATCH v3 04/36] libsepol: add libfuzz based fuzzer for reading binary policies Christian Göttsche
@ 2021-12-09 16:48     ` Christian Göttsche
  2021-12-09 16:48     ` [PATCH v3 06/36] libsepol: use logging framework in conditional.c Christian Göttsche
                       ` (31 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:48 UTC (permalink / raw)
  To: selinux

Limit the maximum length of read sizes, like string length of module
version and name or keys and number of symtab entries.  This avoids the
fuzzer to report oom events for huge allocations (it also improves the
number of executions per seconds of the fuzzer).

This change only affects the fuzzer build.

    ==15211== ERROR: libFuzzer: out-of-memory (malloc(3115956666))
       To change the out-of-memory limit use -rss_limit_mb=<N>

        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aa143 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa143)
        #7 0x5259cb in malloc (./out/binpolicy-fuzzer+0x5259cb)
        #8 0x59d307 in str_read ./libsepol/src/services.c:1746:8
        #9 0x585b97 in perm_read ./libsepol/src/policydb.c:2063:5
        #10 0x581f8a in common_read ./libsepol/src/policydb.c:2119:7
        #11 0x576681 in policydb_read ./libsepol/src/policydb.c:4417:8
        #12 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
        #13 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #14 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #15 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #16 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #17 0x7fe1ec88a7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #18 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

    ==12683== ERROR: libFuzzer: out-of-memory (malloc(2526451450))
       To change the out-of-memory limit use -rss_limit_mb=<N>

        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aa143 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa143)
        #7 0x5259cb in malloc (./out/binpolicy-fuzzer+0x5259cb)
        #8 0x575f8a in policydb_read ./libsepol/src/policydb.c:4356:18
        #9 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
        #10 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #11 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #12 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #13 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #14 0x7fa737b377ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #15 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/private.h | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/libsepol/src/private.h b/libsepol/src/private.h
index 71287282..6146f59f 100644
--- a/libsepol/src/private.h
+++ b/libsepol/src/private.h
@@ -44,7 +44,12 @@
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
 
-#define is_saturated(x) (x == (typeof(x))-1)
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+# define is_saturated(x) (x == (typeof(x))-1 || (x) > (1U << 16))
+#else
+# define is_saturated(x) (x == (typeof(x))-1)
+#endif
+
 #define zero_or_saturated(x) ((x == 0) || is_saturated(x))
 
 #define spaceship_cmp(a, b) (((a) > (b)) - ((a) < (b)))
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 06/36] libsepol: use logging framework in conditional.c
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (4 preceding siblings ...)
  2021-12-09 16:48     ` [PATCH v3 05/36] libsepol/fuzz: limit element sizes for fuzzing Christian Göttsche
@ 2021-12-09 16:48     ` Christian Göttsche
  2021-12-09 16:48     ` [PATCH v3 07/36] libsepol: use logging framework in ebitmap.c Christian Göttsche
                       ` (30 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:48 UTC (permalink / raw)
  To: selinux

Use the internal logging framework instead of directly writing to
stdout as it might be undesired to do so within a library.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   replace INFO calls by WARN since they are reasons of failure
---
 libsepol/src/conditional.c | 30 +++++++++++-------------------
 1 file changed, 11 insertions(+), 19 deletions(-)

diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c
index 037dc7e2..1edac65d 100644
--- a/libsepol/src/conditional.c
+++ b/libsepol/src/conditional.c
@@ -25,6 +25,7 @@
 #include <sepol/policydb/conditional.h>
 
 #include "private.h"
+#include "debug.h"
 
 /* move all type rules to top of t/f lists to help kernel on evaluation */
 static void cond_optimize(cond_av_list_t ** l)
@@ -314,8 +315,7 @@ static int evaluate_cond_node(policydb_t * p, cond_node_t * node)
 	if (new_state != node->cur_state) {
 		node->cur_state = new_state;
 		if (new_state == -1)
-			printf
-			    ("expression result was undefined - disabling all rules.\n");
+			WARN(NULL, "expression result was undefined - disabling all rules.\n");
 		/* turn the rules on or off */
 		for (cur = node->true_list; cur != NULL; cur = cur->next) {
 			if (new_state <= 0) {
@@ -368,8 +368,7 @@ int cond_normalize_expr(policydb_t * p, cond_node_t * cn)
 		if (ne) {
 			ne->next = NULL;
 		} else {	/* ne should never be NULL */
-			printf
-			    ("Found expr with no bools and only a ! - this should never happen.\n");
+			ERR(NULL, "Found expr with no bools and only a ! - this should never happen.\n");
 			return -1;
 		}
 		/* swap the true and false lists */
@@ -421,8 +420,7 @@ int cond_normalize_expr(policydb_t * p, cond_node_t * cn)
 			}
 			k = cond_evaluate_expr(p, cn->expr);
 			if (k == -1) {
-				printf
-				    ("While testing expression, expression result "
+				ERR(NULL, "While testing expression, expression result "
 				     "was undefined - this should never happen.\n");
 				return -1;
 			}
@@ -635,8 +633,7 @@ static int cond_insertf(avtab_t * a
 	 */
 	if (k->specified & AVTAB_TYPE) {
 		if (avtab_search(&p->te_avtab, k)) {
-			printf
-			    ("security: type rule already exists outside of a conditional.");
+			WARN(NULL, "security: type rule already exists outside of a conditional.");
 			goto err;
 		}
 		/*
@@ -652,8 +649,7 @@ static int cond_insertf(avtab_t * a
 			if (node_ptr) {
 				if (avtab_search_node_next
 				    (node_ptr, k->specified)) {
-					printf
-					    ("security: too many conflicting type rules.");
+					ERR(NULL, "security: too many conflicting type rules.");
 					goto err;
 				}
 				found = 0;
@@ -664,15 +660,13 @@ static int cond_insertf(avtab_t * a
 					}
 				}
 				if (!found) {
-					printf
-					    ("security: conflicting type rules.\n");
+					ERR(NULL, "security: conflicting type rules.\n");
 					goto err;
 				}
 			}
 		} else {
 			if (avtab_search(&p->te_cond_avtab, k)) {
-				printf
-				    ("security: conflicting type rules when adding type rule for true.\n");
+				ERR(NULL, "security: conflicting type rules when adding type rule for true.\n");
 				goto err;
 			}
 		}
@@ -680,7 +674,7 @@ static int cond_insertf(avtab_t * a
 
 	node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d);
 	if (!node_ptr) {
-		printf("security: could not insert rule.");
+		ERR(NULL, "security: could not insert rule.");
 		goto err;
 	}
 	node_ptr->parse_context = (void *)1;
@@ -742,14 +736,12 @@ static int cond_read_av_list(policydb_t * p, void *fp,
 static int expr_isvalid(policydb_t * p, cond_expr_t * expr)
 {
 	if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) {
-		printf
-		    ("security: conditional expressions uses unknown operator.\n");
+		WARN(NULL, "security: conditional expressions uses unknown operator.\n");
 		return 0;
 	}
 
 	if (expr->bool > p->p_bools.nprim) {
-		printf
-		    ("security: conditional expressions uses unknown bool.\n");
+		WARN(NULL, "security: conditional expressions uses unknown bool.\n");
 		return 0;
 	}
 	return 1;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 07/36] libsepol: use logging framework in ebitmap.c
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (5 preceding siblings ...)
  2021-12-09 16:48     ` [PATCH v3 06/36] libsepol: use logging framework in conditional.c Christian Göttsche
@ 2021-12-09 16:48     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 08/36] libsepol: use mallocarray wrapper to avoid overflows Christian Göttsche
                       ` (29 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:48 UTC (permalink / raw)
  To: selinux

Use the internal logging framework instead of directly writing to
stdout as it might be undesired to do so within a library.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/ebitmap.c | 27 ++++++++++-----------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/libsepol/src/ebitmap.c b/libsepol/src/ebitmap.c
index 1de3816a..fa728558 100644
--- a/libsepol/src/ebitmap.c
+++ b/libsepol/src/ebitmap.c
@@ -406,8 +406,7 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 	count = le32_to_cpu(buf[2]);
 
 	if (mapsize != MAPSIZE) {
-		printf
-		    ("security: ebitmap: map size %d does not match my size %zu (high bit was %d)\n",
+		ERR(NULL, "security: ebitmap: map size %d does not match my size %zu (high bit was %d)\n",
 		     mapsize, MAPSIZE, e->highbit);
 		goto bad;
 	}
@@ -416,8 +415,7 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 		goto ok;
 	}
 	if (e->highbit & (MAPSIZE - 1)) {
-		printf
-		    ("security: ebitmap: high bit (%d) is not a multiple of the map size (%zu)\n",
+		ERR(NULL, "security: ebitmap: high bit (%d) is not a multiple of the map size (%zu)\n",
 		     e->highbit, MAPSIZE);
 		goto bad;
 	}
@@ -429,12 +427,12 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 	for (i = 0; i < count; i++) {
 		rc = next_entry(buf, fp, sizeof(uint32_t));
 		if (rc < 0) {
-			printf("security: ebitmap: truncated map\n");
+			ERR(NULL, "security: ebitmap: truncated map\n");
 			goto bad;
 		}
 		n = (ebitmap_node_t *) malloc(sizeof(ebitmap_node_t));
 		if (!n) {
-			printf("security: ebitmap: out of memory\n");
+			ERR(NULL, "security: ebitmap: out of memory\n");
 			rc = -ENOMEM;
 			goto bad;
 		}
@@ -443,34 +441,30 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 		n->startbit = le32_to_cpu(buf[0]);
 
 		if (n->startbit & (MAPSIZE - 1)) {
-			printf
-			    ("security: ebitmap start bit (%d) is not a multiple of the map size (%zu)\n",
+			ERR(NULL, "security: ebitmap start bit (%d) is not a multiple of the map size (%zu)\n",
 			     n->startbit, MAPSIZE);
 			goto bad_free;
 		}
 		if (n->startbit > (e->highbit - MAPSIZE)) {
-			printf
-			    ("security: ebitmap start bit (%d) is beyond the end of the bitmap (%zu)\n",
+			ERR(NULL, "security: ebitmap start bit (%d) is beyond the end of the bitmap (%zu)\n",
 			     n->startbit, (e->highbit - MAPSIZE));
 			goto bad_free;
 		}
 		rc = next_entry(&map, fp, sizeof(uint64_t));
 		if (rc < 0) {
-			printf("security: ebitmap: truncated map\n");
+			ERR(NULL, "security: ebitmap: truncated map\n");
 			goto bad_free;
 		}
 		n->map = le64_to_cpu(map);
 
 		if (!n->map) {
-			printf
-			    ("security: ebitmap: null map in ebitmap (startbit %d)\n",
+			ERR(NULL, "security: ebitmap: null map in ebitmap (startbit %d)\n",
 			     n->startbit);
 			goto bad_free;
 		}
 		if (l) {
 			if (n->startbit <= l->startbit) {
-				printf
-				    ("security: ebitmap: start bit %d comes after start bit %d\n",
+				ERR(NULL, "security: ebitmap: start bit %d comes after start bit %d\n",
 				     n->startbit, l->startbit);
 				goto bad_free;
 			}
@@ -481,8 +475,7 @@ int ebitmap_read(ebitmap_t * e, void *fp)
 		l = n;
 	}
 	if (count && l->startbit + MAPSIZE != e->highbit) {
-		printf
-		    ("security: ebitmap: high bit %u has not the expected value %zu\n",
+		ERR(NULL, "security: ebitmap: high bit %u has not the expected value %zu\n",
 		     e->highbit, l->startbit + MAPSIZE);
 		goto bad;
 	}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 08/36] libsepol: use mallocarray wrapper to avoid overflows
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (6 preceding siblings ...)
  2021-12-09 16:48     ` [PATCH v3 07/36] libsepol: use logging framework in ebitmap.c Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 09/36] libsepol: use reallocarray " Christian Göttsche
                       ` (28 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Use a wrapper to guard `malloc(a * b)` type allocations, to detect
multiplication overflows, which result in too few memory being
allocated.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/conditional.c   | 2 +-
 libsepol/src/expand.c        | 4 ++--
 libsepol/src/hashtab.c       | 4 +++-
 libsepol/src/link.c          | 3 ++-
 libsepol/src/module.c        | 4 ++--
 libsepol/src/module_to_cil.c | 4 ++--
 libsepol/src/optimize.c      | 6 ++++--
 libsepol/src/policydb.c      | 6 +++---
 libsepol/src/private.h       | 9 +++++++++
 libsepol/src/services.c      | 6 +++---
 libsepol/src/sidtab.c        | 3 ++-
 libsepol/src/user_record.c   | 3 ++-
 libsepol/src/write.c         | 2 +-
 13 files changed, 36 insertions(+), 20 deletions(-)

diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c
index 1edac65d..cc3f4d82 100644
--- a/libsepol/src/conditional.c
+++ b/libsepol/src/conditional.c
@@ -522,7 +522,7 @@ int cond_init_bool_indexes(policydb_t * p)
 	if (p->bool_val_to_struct)
 		free(p->bool_val_to_struct);
 	p->bool_val_to_struct = (cond_bool_datum_t **)
-	    malloc(p->p_bools.nprim * sizeof(cond_bool_datum_t *));
+	    mallocarray(p->p_bools.nprim, sizeof(cond_bool_datum_t *));
 	if (!p->bool_val_to_struct)
 		return -1;
 	return 0;
diff --git a/libsepol/src/expand.c b/libsepol/src/expand.c
index a6a466f7..8a7259a0 100644
--- a/libsepol/src/expand.c
+++ b/libsepol/src/expand.c
@@ -3146,9 +3146,9 @@ int expand_module(sepol_handle_t * handle,
 		goto cleanup;
 
 	/* Build the type<->attribute maps and remove attributes. */
-	state.out->attr_type_map = malloc(state.out->p_types.nprim *
+	state.out->attr_type_map = mallocarray(state.out->p_types.nprim,
 					  sizeof(ebitmap_t));
-	state.out->type_attr_map = malloc(state.out->p_types.nprim *
+	state.out->type_attr_map = mallocarray(state.out->p_types.nprim,
 					  sizeof(ebitmap_t));
 	if (!state.out->attr_type_map || !state.out->type_attr_map) {
 		ERR(handle, "Out of memory!");
diff --git a/libsepol/src/hashtab.c b/libsepol/src/hashtab.c
index 21143b76..2eb35212 100644
--- a/libsepol/src/hashtab.c
+++ b/libsepol/src/hashtab.c
@@ -32,6 +32,8 @@
 #include <string.h>
 #include <sepol/policydb/hashtab.h>
 
+#include "private.h"
+
 hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h,
 						     const_hashtab_key_t key),
 			 int (*keycmp) (hashtab_t h,
@@ -52,7 +54,7 @@ hashtab_t hashtab_create(unsigned int (*hash_value) (hashtab_t h,
 	p->nel = 0;
 	p->hash_value = hash_value;
 	p->keycmp = keycmp;
-	p->htable = (hashtab_ptr_t *) malloc(sizeof(hashtab_ptr_t) * size);
+	p->htable = (hashtab_ptr_t *) mallocarray(size, sizeof(hashtab_ptr_t));
 	if (p->htable == NULL) {
 		free(p);
 		return NULL;
diff --git a/libsepol/src/link.c b/libsepol/src/link.c
index b14240d5..dfcb0673 100644
--- a/libsepol/src/link.c
+++ b/libsepol/src/link.c
@@ -34,6 +34,7 @@
 #include <assert.h>
 
 #include "debug.h"
+#include "private.h"
 
 #undef min
 #define min(a,b) (((a) < (b)) ? (a) : (b))
@@ -1680,7 +1681,7 @@ static int copy_scope_index(scope_index_t * src, scope_index_t * dest,
 	}
 
 	/* next copy the enabled permissions data  */
-	if ((dest->class_perms_map = malloc(largest_mapped_class_value *
+	if ((dest->class_perms_map = mallocarray(largest_mapped_class_value,
 					    sizeof(*dest->class_perms_map))) ==
 	    NULL) {
 		goto cleanup;
diff --git a/libsepol/src/module.c b/libsepol/src/module.c
index b718751e..d93d08a2 100644
--- a/libsepol/src/module.c
+++ b/libsepol/src/module.c
@@ -409,14 +409,14 @@ static int module_package_read_offsets(sepol_module_package_t * mod,
 		goto err;
 	}
 
-	off = (size_t *) malloc((nsec + 1) * sizeof(size_t));
+	off = (size_t *) mallocarray(nsec + 1, sizeof(size_t));
 	if (!off) {
 		ERR(file->handle, "out of memory");
 		goto err;
 	}
 
 	free(buf);
-	buf = malloc(sizeof(uint32_t) * nsec);
+	buf = mallocarray(nsec, sizeof(uint32_t));
 	if (!buf) {
 		ERR(file->handle, "out of memory");
 		goto err;
diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c
index b231d7f8..33a11a15 100644
--- a/libsepol/src/module_to_cil.c
+++ b/libsepol/src/module_to_cil.c
@@ -430,7 +430,7 @@ static int stack_init(struct stack **stack)
 		goto exit;
 	}
 
-	s->stack = malloc(sizeof(*s->stack) * STACK_SIZE);
+	s->stack = mallocarray(STACK_SIZE, sizeof(*s->stack));
 	if (s->stack == NULL) {
 		goto exit;
 	}
@@ -1008,7 +1008,7 @@ static int ebitmap_to_names(struct ebitmap *map, char **vals_to_names, char ***n
 		goto exit;
 	}
 
-	name_arr = malloc(sizeof(*name_arr) * num);
+	name_arr = mallocarray(num, sizeof(*name_arr));
 	if (name_arr == NULL) {
 		log_err("Out of memory");
 		rc = -1;
diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c
index 6826155c..f8298fb7 100644
--- a/libsepol/src/optimize.c
+++ b/libsepol/src/optimize.c
@@ -31,6 +31,8 @@
 #include <sepol/policydb/policydb.h>
 #include <sepol/policydb/conditional.h>
 
+#include "private.h"
+
 #define TYPE_VEC_INIT_SIZE 16
 
 struct type_vec {
@@ -42,7 +44,7 @@ static int type_vec_init(struct type_vec *v)
 {
 	v->capacity = TYPE_VEC_INIT_SIZE;
 	v->count = 0;
-	v->types = malloc(v->capacity * sizeof(*v->types));
+	v->types = mallocarray(v->capacity, sizeof(*v->types));
 	if (!v->types)
 		return -1;
 	return 0;
@@ -93,7 +95,7 @@ static struct type_vec *build_type_map(const policydb_t *p)
 {
 	unsigned int i, k;
 	ebitmap_node_t *n;
-	struct type_vec *map = malloc(p->p_types.nprim * sizeof(*map));
+	struct type_vec *map = mallocarray(p->p_types.nprim, sizeof(*map));
 	if (!map)
 		return NULL;
 
diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 587ba64a..dcea1807 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -4111,7 +4111,7 @@ static int scope_read(policydb_t * p, int symnum, struct policy_file *fp)
 		goto cleanup;
 	}
 	if ((scope->decl_ids =
-	     malloc(scope->decl_ids_len * sizeof(uint32_t))) == NULL) {
+	     mallocarray(scope->decl_ids_len, sizeof(uint32_t))) == NULL) {
 		goto cleanup;
 	}
 	rc = next_entry(scope->decl_ids, fp, sizeof(uint32_t) * scope->decl_ids_len);
@@ -4500,8 +4500,8 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose)
 	}
 
 	if (policy_type == POLICY_KERN) {
-		p->type_attr_map = malloc(p->p_types.nprim * sizeof(ebitmap_t));
-		p->attr_type_map = malloc(p->p_types.nprim * sizeof(ebitmap_t));
+		p->type_attr_map = mallocarray(p->p_types.nprim, sizeof(ebitmap_t));
+		p->attr_type_map = mallocarray(p->p_types.nprim, sizeof(ebitmap_t));
 		if (!p->type_attr_map || !p->attr_type_map)
 			goto bad;
 		for (i = 0; i < p->p_types.nprim; i++) {
diff --git a/libsepol/src/private.h b/libsepol/src/private.h
index 6146f59f..d3d65a57 100644
--- a/libsepol/src/private.h
+++ b/libsepol/src/private.h
@@ -83,3 +83,12 @@ extern int next_entry(void *buf, struct policy_file *fp, size_t bytes);
 extern size_t put_entry(const void *ptr, size_t size, size_t n,
 		        struct policy_file *fp);
 extern int str_read(char **strp, struct policy_file *fp, size_t len);
+
+static inline void* mallocarray(size_t nmemb, size_t size) {
+	if (size && nmemb > (size_t)-1 / size) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	return malloc(nmemb * size);
+}
diff --git a/libsepol/src/services.c b/libsepol/src/services.c
index 3407058f..edcdde21 100644
--- a/libsepol/src/services.c
+++ b/libsepol/src/services.c
@@ -712,7 +712,7 @@ mls_ops:
 	 * Generate the same number of answer buffer entries as expression
 	 * buffers (as there will never be more).
 	 */
-	answer_list = malloc(expr_count * sizeof(*answer_list));
+	answer_list = mallocarray(expr_count, sizeof(*answer_list));
 	if (!answer_list) {
 		ERR(NULL, "failed to allocate answer stack");
 		rc = -ENOMEM;
@@ -2163,7 +2163,7 @@ int sepol_get_user_sids(sepol_security_id_t fromsid,
 	}
 	usercon.user = user->s.value;
 
-	mysids = malloc(maxnel * sizeof(sepol_security_id_t));
+	mysids = mallocarray(maxnel, sizeof(sepol_security_id_t));
 	if (!mysids) {
 		rc = -ENOMEM;
 		goto out;
@@ -2199,7 +2199,7 @@ int sepol_get_user_sids(sepol_security_id_t fromsid,
 			} else {
 				maxnel += SIDS_NEL;
 				mysids2 =
-				    malloc(maxnel *
+				    mallocarray(maxnel,
 					   sizeof(sepol_security_id_t));
 
 				if (!mysids2) {
diff --git a/libsepol/src/sidtab.c b/libsepol/src/sidtab.c
index 255e0725..adeae6eb 100644
--- a/libsepol/src/sidtab.c
+++ b/libsepol/src/sidtab.c
@@ -15,6 +15,7 @@
 #include <sepol/policydb/sidtab.h>
 
 #include "flask.h"
+#include "private.h"
 
 #define SIDTAB_HASH(sid) \
 (sid & SIDTAB_HASH_MASK)
@@ -27,7 +28,7 @@ int sepol_sidtab_init(sidtab_t * s)
 {
 	int i;
 
-	s->htable = malloc(sizeof(sidtab_ptr_t) * SIDTAB_SIZE);
+	s->htable = mallocarray(SIDTAB_SIZE, sizeof(sidtab_ptr_t));
 	if (!s->htable)
 		return -ENOMEM;
 	for (i = 0; i < SIDTAB_SIZE; i++)
diff --git a/libsepol/src/user_record.c b/libsepol/src/user_record.c
index ac520060..c1356a6b 100644
--- a/libsepol/src/user_record.c
+++ b/libsepol/src/user_record.c
@@ -4,6 +4,7 @@
 
 #include "user_internal.h"
 #include "debug.h"
+#include "private.h"
 
 struct sepol_user {
 	/* This user's name */
@@ -265,7 +266,7 @@ int sepol_user_get_roles(sepol_handle_t * handle,
 
 	unsigned int i;
 	const char **tmp_roles =
-	    (const char **)malloc(sizeof(char *) * user->num_roles);
+	    (const char **)mallocarray(user->num_roles, sizeof(char *));
 	if (!tmp_roles)
 		goto omem;
 
diff --git a/libsepol/src/write.c b/libsepol/src/write.c
index 3bd034d6..9df5b0bd 100644
--- a/libsepol/src/write.c
+++ b/libsepol/src/write.c
@@ -2117,7 +2117,7 @@ static int scope_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr)
 		 * buffer.  this would have been easier with C99's
 		 * dynamic arrays... */
 		rc = POLICYDB_ERROR;
-		dyn_buf = malloc(items * sizeof(*dyn_buf));
+		dyn_buf = mallocarray(items, sizeof(*dyn_buf));
 		if (!dyn_buf)
 			goto err;
 		buf = dyn_buf;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 09/36] libsepol: use reallocarray wrapper to avoid overflows
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (7 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 08/36] libsepol: use mallocarray wrapper to avoid overflows Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 10/36] libsepol: add checks for read sizes Christian Göttsche
                       ` (27 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Use a wrapper to guard `realloc(p, a * b)` type allocations, to detect
multiplication overflows, which result in too few memory being
allocated.

Use a custom implementation if the used C library does not offer one.

Also use temporary variables for realloc(3) results in add_i_to_a() and
fp_to_buffer().

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/Makefile           |  6 ++++++
 libsepol/src/kernel_to_common.c |  4 ++--
 libsepol/src/module_to_cil.c    |  9 +++++----
 libsepol/src/optimize.c         |  5 +++--
 libsepol/src/private.h          | 11 +++++++++++
 libsepol/src/services.c         |  6 +++---
 libsepol/src/user_record.c      |  5 +++--
 libsepol/src/users.c            | 12 ++++++------
 libsepol/src/util.c             | 11 +++++++----
 9 files changed, 46 insertions(+), 23 deletions(-)

diff --git a/libsepol/src/Makefile b/libsepol/src/Makefile
index dc8b1773..13410c67 100644
--- a/libsepol/src/Makefile
+++ b/libsepol/src/Makefile
@@ -29,6 +29,12 @@ LOBJS += $(sort $(patsubst %.c,%.lo,$(sort $(wildcard $(CILDIR)/src/*.c)) $(CIL_
 override CFLAGS += -I$(CILDIR)/include
 endif
 
+# check for reallocarray(3) availability
+H := \#
+ifeq (yes,$(shell printf '${H}define _GNU_SOURCE\n${H}include <stdlib.h>\nint main(void){void*p=reallocarray(NULL, 1, sizeof(char));return 0;}' | $(CC) -x c -o /dev/null - >/dev/null 2>&1 && echo yes))
+override CFLAGS += -DHAVE_REALLOCARRAY
+endif
+
 LD_SONAME_FLAGS=-soname,$(LIBSO),--version-script=$(LIBMAP),-z,defs
 
 LN=ln
diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c
index a7453d3c..51df8c25 100644
--- a/libsepol/src/kernel_to_common.c
+++ b/libsepol/src/kernel_to_common.c
@@ -161,7 +161,7 @@ int strs_add(struct strs *strs, char *s)
 		char **new;
 		unsigned i = strs->size;
 		strs->size *= 2;
-		new = realloc(strs->list, sizeof(char *)*strs->size);
+		new = reallocarray(strs->list, strs->size, sizeof(char *));
 		if (!new) {
 			sepol_log_err("Out of memory");
 			return -1;
@@ -220,7 +220,7 @@ int strs_add_at_index(struct strs *strs, char *s, unsigned index)
 		while (index >= strs->size) {
 			strs->size *= 2;
 		}
-		new = realloc(strs->list, sizeof(char *)*strs->size);
+		new = reallocarray(strs->list, strs->size, sizeof(char *));
 		if (!new) {
 			sepol_log_err("Out of memory");
 			return -1;
diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c
index 33a11a15..5f762aba 100644
--- a/libsepol/src/module_to_cil.c
+++ b/libsepol/src/module_to_cil.c
@@ -453,7 +453,7 @@ static int stack_push(struct stack *stack, void *ptr)
 	void *new_stack;
 
 	if (stack->pos + 1 == stack->size) {
-		new_stack = realloc(stack->stack, sizeof(*stack->stack) * (stack->size * 2));
+		new_stack = reallocarray(stack->stack, stack->size * 2, sizeof(*stack->stack));
 		if (new_stack == NULL) {
 			goto exit;
 		}
@@ -4117,7 +4117,7 @@ exit:
 static int fp_to_buffer(FILE *fp, char **data, size_t *data_len)
 {
 	int rc = -1;
-	char *d = NULL;
+	char *d = NULL, *d_tmp;
 	size_t d_len = 0;
 	size_t read_len = 0;
 	size_t max_len = 1 << 17; // start at 128KB, this is enough to hold about half of all the existing pp files
@@ -4133,12 +4133,13 @@ static int fp_to_buffer(FILE *fp, char **data, size_t *data_len)
 		d_len += read_len;
 		if (d_len == max_len) {
 			max_len *= 2;
-			d = realloc(d, max_len);
-			if (d == NULL) {
+			d_tmp = realloc(d, max_len);
+			if (d_tmp == NULL) {
 				log_err("Out of memory");
 				rc = -1;
 				goto exit;
 			}
+			d = d_tmp;
 		}
 	}
 
diff --git a/libsepol/src/optimize.c b/libsepol/src/optimize.c
index f8298fb7..8a048702 100644
--- a/libsepol/src/optimize.c
+++ b/libsepol/src/optimize.c
@@ -59,8 +59,9 @@ static int type_vec_append(struct type_vec *v, uint32_t type)
 {
 	if (v->capacity == v->count) {
 		unsigned int new_capacity = v->capacity * 2;
-		uint32_t *new_types = realloc(v->types,
-					      new_capacity * sizeof(*v->types));
+		uint32_t *new_types = reallocarray(v->types,
+						   new_capacity,
+						   sizeof(*v->types));
 		if (!new_types)
 			return -1;
 
diff --git a/libsepol/src/private.h b/libsepol/src/private.h
index d3d65a57..a8cc1472 100644
--- a/libsepol/src/private.h
+++ b/libsepol/src/private.h
@@ -92,3 +92,14 @@ static inline void* mallocarray(size_t nmemb, size_t size) {
 
 	return malloc(nmemb * size);
 }
+
+#ifndef HAVE_REALLOCARRAY
+static inline void* reallocarray(void *ptr, size_t nmemb, size_t size) {
+	if (size && nmemb > (size_t)-1 / size) {
+		errno = ENOMEM;
+		return NULL;
+	}
+
+	return realloc(ptr, nmemb * size);
+}
+#endif
diff --git a/libsepol/src/services.c b/libsepol/src/services.c
index edcdde21..0f36ac53 100644
--- a/libsepol/src/services.c
+++ b/libsepol/src/services.c
@@ -94,7 +94,7 @@ static void push(char *expr_ptr)
 		else
 			new_stack_len = stack_len * 2;
 
-		new_stack = realloc(stack, new_stack_len * sizeof(*stack));
+		new_stack = reallocarray(stack, new_stack_len, sizeof(*stack));
 		if (!new_stack) {
 			ERR(NULL, "unable to allocate stack space");
 			return;
@@ -449,8 +449,8 @@ static int constraint_expr_eval_reason(context_struct_t *scontext,
 			else
 				new_expr_list_len = expr_list_len * 2;
 
-			new_expr_list = realloc(expr_list,
-					new_expr_list_len * sizeof(*expr_list));
+			new_expr_list = reallocarray(expr_list,
+					new_expr_list_len, sizeof(*expr_list));
 			if (!new_expr_list) {
 				ERR(NULL, "failed to allocate expr buffer stack");
 				rc = -ENOMEM;
diff --git a/libsepol/src/user_record.c b/libsepol/src/user_record.c
index c1356a6b..404fa3a8 100644
--- a/libsepol/src/user_record.c
+++ b/libsepol/src/user_record.c
@@ -183,8 +183,9 @@ int sepol_user_add_role(sepol_handle_t * handle,
 	if (!role_cp)
 		goto omem;
 
-	roles_realloc = realloc(user->roles,
-				sizeof(char *) * (user->num_roles + 1));
+	roles_realloc = reallocarray(user->roles,
+				     user->num_roles + 1,
+				     sizeof(char *));
 	if (!roles_realloc)
 		goto omem;
 
diff --git a/libsepol/src/users.c b/libsepol/src/users.c
index b895b7f5..a7406214 100644
--- a/libsepol/src/users.c
+++ b/libsepol/src/users.c
@@ -226,17 +226,17 @@ int sepol_user_modify(sepol_handle_t * handle,
 		void *tmp_ptr;
 
 		/* Ensure reverse lookup array has enough space */
-		tmp_ptr = realloc(policydb->user_val_to_struct,
-				  (policydb->p_users.nprim +
-				   1) * sizeof(user_datum_t *));
+		tmp_ptr = reallocarray(policydb->user_val_to_struct,
+				  policydb->p_users.nprim + 1,
+				  sizeof(user_datum_t *));
 		if (!tmp_ptr)
 			goto omem;
 		policydb->user_val_to_struct = tmp_ptr;
 		policydb->user_val_to_struct[policydb->p_users.nprim] = NULL;
 
-		tmp_ptr = realloc(policydb->sym_val_to_name[SYM_USERS],
-				  (policydb->p_users.nprim +
-				   1) * sizeof(char *));
+		tmp_ptr = reallocarray(policydb->sym_val_to_name[SYM_USERS],
+				  policydb->p_users.nprim + 1,
+				  sizeof(char *));
 		if (!tmp_ptr)
 			goto omem;
 		policydb->sym_val_to_name[SYM_USERS] = tmp_ptr;
diff --git a/libsepol/src/util.c b/libsepol/src/util.c
index 902c63c5..b7230564 100644
--- a/libsepol/src/util.c
+++ b/libsepol/src/util.c
@@ -40,6 +40,8 @@ struct val_to_name {
  * 0).  Return 0 on success, -1 on out of memory. */
 int add_i_to_a(uint32_t i, uint32_t * cnt, uint32_t ** a)
 {
+	uint32_t *new;
+
 	if (cnt == NULL || a == NULL)
 		return -1;
 
@@ -48,17 +50,18 @@ int add_i_to_a(uint32_t i, uint32_t * cnt, uint32_t ** a)
 	 * than be smart about it, for now we realloc() the array each
 	 * time a new uint32_t is added! */
 	if (*a != NULL)
-		*a = (uint32_t *) realloc(*a, (*cnt + 1) * sizeof(uint32_t));
+		new = (uint32_t *) reallocarray(*a, *cnt + 1, sizeof(uint32_t));
 	else {			/* empty list */
 
 		*cnt = 0;
-		*a = (uint32_t *) malloc(sizeof(uint32_t));
+		new = (uint32_t *) malloc(sizeof(uint32_t));
 	}
-	if (*a == NULL) {
+	if (new == NULL) {
 		return -1;
 	}
-	(*a)[*cnt] = i;
+	new[*cnt] = i;
 	(*cnt)++;
+	*a = new;
 	return 0;
 }
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 10/36] libsepol: add checks for read sizes
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (8 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 09/36] libsepol: use reallocarray " Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 11/36] libsepol: enforce avtab item limit Christian Göttsche
                       ` (26 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Add checks for invalid read sizes from a binary policy to guard
allocations.

The common and class permission counts needs to be limited more strict
otherwise a too high count of common or class permissions can lead to
permission values with a too high value, which can lead to overflows
in shift operations.

In the fuzzer build the value will also be bounded to avoid oom reports.

    ==29857== ERROR: libFuzzer: out-of-memory (malloc(17179868160))
       To change the out-of-memory limit use -rss_limit_mb=<N>

        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aa143 in __asan::asan_malloc(unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa143)
        #7 0x5259cb in malloc (./out/binpolicy-fuzzer+0x5259cb)
        #8 0x580b5d in mallocarray ./libsepol/src/./private.h:93:9
        #9 0x57c2ed in scope_read ./libsepol/src/policydb.c:4120:7
        #10 0x576b0d in policydb_read ./libsepol/src/policydb.c:4462:9
        #11 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
        #12 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #13 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #14 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #15 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #16 0x7ffad6e107ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #17 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

    ==19462== ERROR: libFuzzer: out-of-memory (malloc(18253611008))
       To change the out-of-memory limit use -rss_limit_mb=<N>

        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aa999 in __asan::asan_calloc(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aa999)
        #7 0x525b63 in __interceptor_calloc (./out/binpolicy-fuzzer+0x525b63)
        #8 0x570938 in policydb_index_others ./libsepol/src/policydb.c:1245:6
        #9 0x5771f3 in policydb_read ./src/policydb.c:4481:6
        #10 0x55a214 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:26:6
        #11 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #12 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #13 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #14 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #15 0x7f4d933157ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #16 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
v3:
   - use PERM_SYMTAB_SIZE instead of bare 32 as limit
---
 libsepol/src/policydb.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index dcea1807..0b2edf51 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -2103,6 +2103,8 @@ static int common_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
 	if (symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE))
 		goto bad;
 	comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+	if (comdatum->permissions.nprim > PERM_SYMTAB_SIZE)
+		goto bad;
 	nel = le32_to_cpu(buf[3]);
 
 	key = malloc(len + 1);
@@ -2251,6 +2253,8 @@ static int class_read(policydb_t * p, hashtab_t h, struct policy_file *fp)
 	if (symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE))
 		goto bad;
 	cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+	if (cladatum->permissions.nprim > PERM_SYMTAB_SIZE)
+		goto bad;
 	nel = le32_to_cpu(buf[4]);
 
 	ncons = le32_to_cpu(buf[5]);
@@ -3980,6 +3984,8 @@ static int avrule_decl_read(policydb_t * p, avrule_decl_t * decl,
 		if (rc < 0) 
 			return -1;
 		nprim = le32_to_cpu(buf[0]);
+		if (is_saturated(nprim))
+			return -1;
 		nel = le32_to_cpu(buf[1]);
 		for (j = 0; j < nel; j++) {
 			if (read_f[i] (p, decl->symtab[i].table, fp)) {
@@ -4106,7 +4112,7 @@ static int scope_read(policydb_t * p, int symnum, struct policy_file *fp)
 		goto cleanup;
 	scope->scope = le32_to_cpu(buf[0]);
 	scope->decl_ids_len = le32_to_cpu(buf[1]);
-	if (scope->decl_ids_len == 0) {
+	if (zero_or_saturated(scope->decl_ids_len)) {
 		ERR(fp->handle, "invalid scope with no declaration");
 		goto cleanup;
 	}
@@ -4396,6 +4402,8 @@ int policydb_read(policydb_t * p, struct policy_file *fp, unsigned verbose)
 		if (rc < 0)
 			goto bad;
 		nprim = le32_to_cpu(buf[0]);
+		if (is_saturated(nprim))
+			goto bad;
 		nel = le32_to_cpu(buf[1]);
 		if (nel && !nprim) {
 			ERR(fp->handle, "unexpected items in symbol table with no symbol");
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 11/36] libsepol: enforce avtab item limit
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (9 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 10/36] libsepol: add checks for read sizes Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-15 17:39       ` James Carter
  2021-12-09 16:49     ` [PATCH v3 12/36] libsepol: clean memory on conditional insertion failure Christian Göttsche
                       ` (25 subsequent siblings)
  36 siblings, 1 reply; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Check the current item count does not exceed the maximum allowed to
avoid stack overflows.

    ==33660==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fa64b8fc070 at pc 0x0000005acba0 bp 0x7ffc1f0b2870 sp 0x7ffc1f0b2868
    READ of size 4 at 0x7fa64b8fc070 thread T0
        #0 0x5acb9f in avtab_read_item ./libsepol/src/avtab.c:507:18
        #1 0x5acec4 in avtab_read ./libsepol/src/avtab.c:611:8
        #2 0x576ae3 in policydb_read ./libsepol/src/policydb.c:4433:7
        #3 0x55a1fe in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:24:6
        #4 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #5 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #6 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #7 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #8 0x7fa64cc867ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #9 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

    Address 0x7fa64b8fc070 is located in stack of thread T0 at offset 112 in frame
        #0 0x5aabdf in avtab_read_item ./libsepol/src/avtab.c:437

      This frame has 6 object(s):
        [32, 33) 'buf8' (line 438)
        [48, 56) 'buf16' (line 439)
        [80, 112) 'buf32' (line 440) <== Memory access at offset 112 overflows this variable
        [144, 152) 'key' (line 441)
        [176, 192) 'datum' (line 442)
        [208, 244) 'xperms' (line 443)
    HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
          (longjmp and C++ exceptions *are* supported)
    SUMMARY: AddressSanitizer: stack-buffer-overflow ./libsepol/src/avtab.c:507:18 in avtab_read_item
    Shadow bytes around the buggy address:
      0x0ff5497177b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff5497177c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff5497177d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff5497177e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff5497177f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    =>0x0ff549717800: f1 f1 f1 f1 01 f2 00 f2 f2 f2 00 00 00 00[f2]f2
      0x0ff549717810: f2 f2 00 f2 f2 f2 00 00 f2 f2 00 00 00 00 04 f3
      0x0ff549717820: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff549717830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff549717840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      0x0ff549717850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    Shadow byte legend (one shadow byte represents 8 application bytes):
      Addressable:           00
      Partially addressable: 01 02 03 04 05 06 07
      Heap left redzone:       fa
      Freed heap region:       fd
      Stack left redzone:      f1
      Stack mid redzone:       f2
      Stack right redzone:     f3
      Stack after return:      f5
      Stack use after scope:   f8
      Global redzone:          f9
      Global init order:       f6
      Poisoned by user:        f7
      Container overflow:      fc
      Array cookie:            ac
      Intra object redzone:    bb
      ASan internal:           fe
      Left alloca redzone:     ca
      Right alloca redzone:    cb
    ==33660==ABORTING

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
v3:
   take zero based numbering of variable items into account
---
 libsepol/src/avtab.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libsepol/src/avtab.c b/libsepol/src/avtab.c
index 46e1e75d..e9b17664 100644
--- a/libsepol/src/avtab.c
+++ b/libsepol/src/avtab.c
@@ -503,6 +503,12 @@ int avtab_read_item(struct policy_file *fp, uint32_t vers, avtab_t * a,
 
 		for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
 			if (val & spec_order[i]) {
+				if (items >= items2) {
+					ERR(fp->handle,
+					    "entry has too many items (%d/%d)",
+					    items + /* zero based numbered */ 1, items2);
+					return -1;
+				}
 				key.specified = spec_order[i] | enabled;
 				datum.data = le32_to_cpu(buf32[items++]);
 				rc = insertf(a, &key, &datum, p);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 12/36] libsepol: clean memory on conditional insertion failure
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (10 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 11/36] libsepol: enforce avtab item limit Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 13/36] libsepol: reject abnormal huge sid ids Christian Göttsche
                       ` (24 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Free the local access vector list on failure as it does not get moved
into the policy structure.
Drop the now redundant, but non-exhaustive, resource cleanup in
cond_insertf().

    Direct leak of 16 byte(s) in 1 object(s) allocated from:
        #0 0x52596d in malloc (./out/binpolicy-fuzzer+0x52596d)
        #1 0x5b30d2 in cond_insertf ./libsepol/src/conditional.c:682:9
        #2 0x5ac218 in avtab_read_item ./libsepol/src/avtab.c:583:10
        #3 0x5b21f4 in cond_read_av_list ./libsepol/src/conditional.c:725:8
        #4 0x5b21f4 in cond_read_node ./libsepol/src/conditional.c:798:7
        #5 0x5b21f4 in cond_read_list ./libsepol/src/conditional.c:847:7
        #6 0x576b6e in policydb_read ./libsepol/src/policydb.c:4436:8
        #7 0x55a1fe in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:24:6
        #8 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #9 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #10 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #11 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #12 0x7f47abeb87ec in __libc_start_main csu/../csu/libc-start.c:332:16

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   drop redundant cleanup in cond_insertf()
---
 libsepol/src/conditional.c | 21 +++++++++------------
 1 file changed, 9 insertions(+), 12 deletions(-)

diff --git a/libsepol/src/conditional.c b/libsepol/src/conditional.c
index cc3f4d82..a3125fdd 100644
--- a/libsepol/src/conditional.c
+++ b/libsepol/src/conditional.c
@@ -634,7 +634,7 @@ static int cond_insertf(avtab_t * a
 	if (k->specified & AVTAB_TYPE) {
 		if (avtab_search(&p->te_avtab, k)) {
 			WARN(NULL, "security: type rule already exists outside of a conditional.");
-			goto err;
+			return -1;
 		}
 		/*
 		 * If we are reading the false list other will be a pointer to
@@ -650,7 +650,7 @@ static int cond_insertf(avtab_t * a
 				if (avtab_search_node_next
 				    (node_ptr, k->specified)) {
 					ERR(NULL, "security: too many conflicting type rules.");
-					goto err;
+					return -1;
 				}
 				found = 0;
 				for (cur = other; cur != NULL; cur = cur->next) {
@@ -661,13 +661,13 @@ static int cond_insertf(avtab_t * a
 				}
 				if (!found) {
 					ERR(NULL, "security: conflicting type rules.\n");
-					goto err;
+					return -1;
 				}
 			}
 		} else {
 			if (avtab_search(&p->te_cond_avtab, k)) {
 				ERR(NULL, "security: conflicting type rules when adding type rule for true.\n");
-				goto err;
+				return -1;
 			}
 		}
 	}
@@ -675,13 +675,13 @@ static int cond_insertf(avtab_t * a
 	node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d);
 	if (!node_ptr) {
 		ERR(NULL, "security: could not insert rule.");
-		goto err;
+		return -1;
 	}
 	node_ptr->parse_context = (void *)1;
 
 	list = malloc(sizeof(cond_av_list_t));
 	if (!list)
-		goto err;
+		return -1;
 	memset(list, 0, sizeof(cond_av_list_t));
 
 	list->node = node_ptr;
@@ -691,11 +691,6 @@ static int cond_insertf(avtab_t * a
 		data->tail->next = list;
 	data->tail = list;
 	return 0;
-
-      err:
-	cond_av_list_destroy(data->head);
-	data->head = NULL;
-	return -1;
 }
 
 static int cond_read_av_list(policydb_t * p, void *fp,
@@ -724,8 +719,10 @@ static int cond_read_av_list(policydb_t * p, void *fp,
 	for (i = 0; i < len; i++) {
 		rc = avtab_read_item(fp, p->policyvers, &p->te_cond_avtab,
 				     cond_insertf, &data);
-		if (rc)
+		if (rc) {
+			cond_av_list_destroy(data.head);
 			return rc;
+		}
 
 	}
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 13/36] libsepol: reject abnormal huge sid ids
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (11 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 12/36] libsepol: clean memory on conditional insertion failure Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 14/36] libsepol: reject invalid filetrans source type Christian Göttsche
                       ` (23 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Check if the sid value is saturated to guard dependent allocations.

    ==19967== ERROR: libFuzzer: out-of-memory (malloc(7784628224))
        #0 0x52dc61 in __sanitizer_print_stack_trace (./out/binpolicy-fuzzer+0x52dc61)
        #1 0x475618 in fuzzer::PrintStackTrace() fuzzer.o
        #2 0x458855 in fuzzer::Fuzzer::HandleMalloc(unsigned long) fuzzer.o
        #3 0x45876a in fuzzer::MallocHook(void const volatile*, unsigned long) fuzzer.o
        #4 0x534557 in __sanitizer::RunMallocHooks(void const*, unsigned long) (./out/binpolicy-fuzzer+0x534557)
        #5 0x4aa7d7 in __asan::Allocator::Allocate(unsigned long, unsigned long, __sanitizer::BufferedStackTrace*, __asan::AllocType, bool) (./out/binpolicy-fuzzer+0x4aa7d7)
        #6 0x4aabe3 in __asan::Allocator::Reallocate(void*, unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aabe3)
        #7 0x4aaa32 in __asan::asan_reallocarray(void*, unsigned long, unsigned long, __sanitizer::BufferedStackTrace*) (./out/binpolicy-fuzzer+0x4aaa32)
        #8 0x525f8e in __interceptor_reallocarray (./out/binpolicy-fuzzer+0x525f8e)
        #9 0x5ebad3 in strs_add_at_index ./libsepol/src/kernel_to_common.c:224:9
        #10 0x5680eb in write_sids_to_conf ./libsepol/src/kernel_to_conf.c:466:8
        #11 0x55c1c0 in write_sid_decl_rules_to_conf ./libsepol/src/kernel_to_conf.c:498:8
        #12 0x55ad36 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3083:7
        #13 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #14 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #15 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #16 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #17 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #18 0x7f085ac657ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #19 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 0b2edf51..a3d34d30 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -2883,6 +2883,8 @@ static int ocontext_read_xen(const struct policydb_compat_info *info,
 				if (rc < 0)
 					return -1;
 				c->sid[0] = le32_to_cpu(buf[0]);
+				if (is_saturated(c->sid[0]))
+					return -1;
 				if (context_read_and_validate
 				    (&c->context[0], p, fp))
 					return -1;
@@ -2994,6 +2996,8 @@ static int ocontext_read_selinux(const struct policydb_compat_info *info,
 				if (rc < 0)
 					return -1;
 				c->sid[0] = le32_to_cpu(buf[0]);
+				if (is_saturated(c->sid[0]))
+					return -1;
 				if (context_read_and_validate
 				    (&c->context[0], p, fp))
 					return -1;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 14/36] libsepol: reject invalid filetrans source type
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (12 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 13/36] libsepol: reject abnormal huge sid ids Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 15/36] libsepol: zero member before potential dereference Christian Göttsche
                       ` (22 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Avoid integer underflow on invalid filetrans source types.

    policydb.c:2658:47: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'unsigned int'
        #0 0x4cf4cb in policydb_filetrans_insert ./libsepol/src/policydb.c:2658:47
        #1 0x4d221a in filename_trans_read_one_compat ./libsepol/src/policydb.c:2691:7
        #2 0x4d221a in filename_trans_read ./libsepol/src/policydb.c:2842:9
        #3 0x4d1370 in policydb_read ./libsepol/src/policydb.c:4447:7
        #4 0x4b1ee3 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:35:6
        #5 0x43f2f3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #6 0x42ae32 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #7 0x430d5b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #8 0x45a1f2 in main (./out/binpolicy-fuzzer+0x45a1f2)
        #9 0x7f8b8923a7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #10 0x407aa9 in _start (./out/binpolicy-fuzzer+0x407aa9)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index a3d34d30..25ffa07c 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -2683,7 +2683,10 @@ static int filename_trans_read_one_compat(policydb_t *p, struct policy_file *fp)
 	if (rc < 0)
 		goto err;
 
-	stype  = le32_to_cpu(buf[0]);
+	stype = le32_to_cpu(buf[0]);
+	if (stype == 0)
+		goto err;
+
 	ttype  = le32_to_cpu(buf[1]);
 	tclass = le32_to_cpu(buf[2]);
 	otype  = le32_to_cpu(buf[3]);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 15/36] libsepol: zero member before potential dereference
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (13 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 14/36] libsepol: reject invalid filetrans source type Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 16/36] libsepol: use size_t for indexes in strs helpers Christian Göttsche
                       ` (21 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

The `next` member might be checked against NULL and dereferenced before
it gets assigned, due to jumps from failure gotos to the cleanup
section.

    ==31017==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x000000579654 bp 0x7ffd3a07d110 sp 0x7ffd3a07d000 T0)
    ==31017==The signal is caused by a READ memory access.
    ==31017==Hint: this fault was caused by a dereference of a high value address (see register values below).  Disassemble the provided pc to learn which register was used.
        #0 0x579654 in filename_trans_read_one ./libsepol/src/policydb.c:2874:55
        #1 0x579654 in filename_trans_read ./libsepol/src/policydb.c:2902:9
        #2 0x5771b7 in policydb_read ./libsepol/src/policydb.c:4509:7
        #3 0x55a1f5 in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:24:6
        #4 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #5 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #6 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #7 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #8 0x7f2a4e7f97ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #9 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libsepol/src/policydb.c b/libsepol/src/policydb.c
index 25ffa07c..79aba3af 100644
--- a/libsepol/src/policydb.c
+++ b/libsepol/src/policydb.c
@@ -2780,6 +2780,7 @@ static int filename_trans_read_one(policydb_t *p, struct policy_file *fp)
 		if (!datum)
 			goto err;
 
+		datum->next = NULL;
 		*dst = datum;
 
 		/* ebitmap_read() will at least init the bitmap */
@@ -2797,7 +2798,6 @@ static int filename_trans_read_one(policydb_t *p, struct policy_file *fp)
 
 		dst = &datum->next;
 	}
-	*dst = NULL;
 
 	if (ndatum > 1 && filename_trans_check_datum(first))
 		goto err;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 16/36] libsepol: use size_t for indexes in strs helpers
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (14 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 15/36] libsepol: zero member before potential dereference Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 17/36] libsepol: do not underflow on short format arguments Christian Göttsche
                       ` (20 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Use size_t, as the strs struct uses it for its size member.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_common.c | 8 ++++----
 libsepol/src/kernel_to_common.h | 4 ++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c
index 51df8c25..47c02d61 100644
--- a/libsepol/src/kernel_to_common.c
+++ b/libsepol/src/kernel_to_common.c
@@ -159,7 +159,7 @@ int strs_add(struct strs *strs, char *s)
 {
 	if (strs->num + 1 > strs->size) {
 		char **new;
-		unsigned i = strs->size;
+		size_t i = strs->size;
 		strs->size *= 2;
 		new = reallocarray(strs->list, strs->size, sizeof(char *));
 		if (!new) {
@@ -212,11 +212,11 @@ char *strs_remove_last(struct strs *strs)
 	return strs->list[strs->num];
 }
 
-int strs_add_at_index(struct strs *strs, char *s, unsigned index)
+int strs_add_at_index(struct strs *strs, char *s, size_t index)
 {
 	if (index >= strs->size) {
 		char **new;
-		unsigned i = strs->size;
+		size_t i = strs->size;
 		while (index >= strs->size) {
 			strs->size *= 2;
 		}
@@ -237,7 +237,7 @@ int strs_add_at_index(struct strs *strs, char *s, unsigned index)
 	return 0;
 }
 
-char *strs_read_at_index(struct strs *strs, unsigned index)
+char *strs_read_at_index(struct strs *strs, size_t index)
 {
 	if (index >= strs->num) {
 		return NULL;
diff --git a/libsepol/src/kernel_to_common.h b/libsepol/src/kernel_to_common.h
index 8aa483fa..e9932d30 100644
--- a/libsepol/src/kernel_to_common.h
+++ b/libsepol/src/kernel_to_common.h
@@ -99,8 +99,8 @@ int strs_add(struct strs *strs, char *s);
 __attribute__ ((format(printf, 2, 4)))
 int strs_create_and_add(struct strs *strs, const char *fmt, int num, ...);
 char *strs_remove_last(struct strs *strs);
-int strs_add_at_index(struct strs *strs, char *s, unsigned index);
-char *strs_read_at_index(struct strs *strs, unsigned index);
+int strs_add_at_index(struct strs *strs, char *s, size_t index);
+char *strs_read_at_index(struct strs *strs, size_t index);
 void strs_sort(struct strs *strs);
 unsigned strs_num_items(struct strs *strs);
 size_t strs_len_items(struct strs *strs);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 17/36] libsepol: do not underflow on short format arguments
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (15 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 16/36] libsepol: use size_t for indexes in strs helpers Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 18/36] libsepol: do not crash on class gaps Christian Göttsche
                       ` (19 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Handle format arguments that do not have a size of at least 2.

    kernel_to_common.c:69:20: runtime error: unsigned integer overflow: 1 - 2 cannot be represented in type 'unsigned long'
        #0 0x557b0b in create_str_helper ./libsepol/src/kernel_to_common.c:69:20
        #1 0x5577b8 in create_str ./libsepol/src/kernel_to_common.c:99:8
        #2 0x56448c in cond_expr_to_str ./libsepol/src/kernel_to_conf.c:82:15
        #3 0x56448c in write_cond_nodes_to_conf ./libsepol/src/kernel_to_conf.c:2103:10
        #4 0x55bd9b in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3171:7
        #5 0x4f9d79 in main ./checkpolicy/checkpolicy.c:684:11
        #6 0x7fe2a342b7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #7 0x41f3a9 in _start (./checkpolicy/checkpolicy+0x41f3a9)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_common.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c
index 47c02d61..152f2816 100644
--- a/libsepol/src/kernel_to_common.c
+++ b/libsepol/src/kernel_to_common.c
@@ -57,7 +57,7 @@ static char *create_str_helper(const char *fmt, int num, va_list vargs)
 	va_list vargs2;
 	char *str = NULL;
 	char *s;
-	size_t len;
+	size_t len, s_len;
 	int i, rc;
 
 	va_copy(vargs2, vargs);
@@ -66,7 +66,8 @@ static char *create_str_helper(const char *fmt, int num, va_list vargs)
 
 	for (i=0; i<num; i++) {
 		s = va_arg(vargs, char *);
-		len += strlen(s) - 2; /* -2 for each %s in fmt */
+		s_len = strlen(s);
+		len += s_len > 1 ? s_len - 2 : 0; /* -2 for each %s in fmt */
 	}
 
 	str = malloc(len);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 18/36] libsepol: do not crash on class gaps
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (16 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 17/36] libsepol: do not underflow on short format arguments Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 19/36] libsepol: do not crash on user gaps Christian Göttsche
                       ` (18 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Handle gaps in the class table while printing a policy configuration.

    ==21763==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000028 (pc 0x00000055b696 bp 0x7ffe69e8ab50 sp 0x7ffe69e8aa60 T0)
    ==21763==The signal is caused by a READ memory access.
    ==21763==Hint: address points to the zero page.
        #0 0x55b696 in constraint_rules_to_strs ./libsepol/src/kernel_to_conf.c:361:14
        #1 0x55ac80 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3063:7
        #2 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #3 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #4 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #5 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #6 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #7 0x7fc60d39e7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #8 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_cil.c  |  9 +++++++++
 libsepol/src/kernel_to_conf.c | 10 ++++++++--
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
index b81cdb22..d9afdda6 100644
--- a/libsepol/src/kernel_to_cil.c
+++ b/libsepol/src/kernel_to_cil.c
@@ -358,6 +358,7 @@ static int constraint_rules_to_strs(struct policydb *pdb, struct strs *mls_strs,
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->constraints) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_constraint_rules_to_strs(pdb, name, class, class->constraints, mls_strs, non_mls_strs);
@@ -383,6 +384,7 @@ static int validatetrans_rules_to_strs(struct policydb *pdb, struct strs *mls_st
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->validatetrans) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_validatetrans_rules_to_strs(pdb, name, class->validatetrans, mls_strs, non_mls_strs);
@@ -461,6 +463,7 @@ static int write_class_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* class */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = pdb->p_class_val_to_name[i];
 		perms = class_or_common_perms_to_str(&class->permissions);
 		if (perms) {
@@ -488,6 +491,7 @@ static int write_class_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* classcommon */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = pdb->p_class_val_to_name[i];
 		if (class->comkey != NULL) {
 			sepol_printf(out, "(classcommon %s %s)\n", name, class->comkey);
@@ -503,6 +507,7 @@ static int write_class_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 	}
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = class->comkey;
 		if (name != NULL) {
 			common = hashtab_search(pdb->p_commons.table, name);
@@ -727,6 +732,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* default_user */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_user != 0) {
 			rc = write_default_user_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -738,6 +744,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* default_role */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_role != 0) {
 			rc = write_default_role_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -749,6 +756,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* default_type */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_type != 0) {
 			rc = write_default_type_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -764,6 +772,7 @@ static int write_default_rules_to_cil(FILE *out, struct policydb *pdb)
 	/* default_range */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_range) {
 			rc = write_default_range_to_cil(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index 460209c8..92a342d1 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -362,7 +362,7 @@ static int constraint_rules_to_strs(struct policydb *pdb, struct strs *mls_strs,
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
-		if (class->constraints) {
+		if (class && class->constraints) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_constraint_rules_to_strs(pdb, name, class, class->constraints, mls_strs, non_mls_strs);
 			if (rc != 0) {
@@ -387,7 +387,7 @@ static int validatetrans_rules_to_strs(struct policydb *pdb, struct strs *mls_st
 
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
-		if (class->validatetrans) {
+		if (class && class->validatetrans) {
 			name = pdb->p_class_val_to_name[i];
 			rc = class_validatetrans_rules_to_strs(pdb, name, class->validatetrans, mls_strs, non_mls_strs);
 			if (rc != 0) {
@@ -555,6 +555,7 @@ static int write_class_and_common_rules_to_conf(FILE *out, struct policydb *pdb)
 	}
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = class->comkey;
 		if (!name) continue;
 		common = hashtab_search(pdb->p_commons.table, name);
@@ -581,6 +582,7 @@ static int write_class_and_common_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* class */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		name = pdb->p_class_val_to_name[i];
 		sepol_printf(out, "class %s", name);
 		if (class->comkey) {
@@ -706,6 +708,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* default_user */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_user != 0) {
 			rc = write_default_user_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -717,6 +720,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* default_role */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_role != 0) {
 			rc = write_default_role_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -728,6 +732,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* default_type */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_type != 0) {
 			rc = write_default_type_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
@@ -743,6 +748,7 @@ static int write_default_rules_to_conf(FILE *out, struct policydb *pdb)
 	/* default_range */
 	for (i=0; i < pdb->p_classes.nprim; i++) {
 		class = pdb->class_val_to_struct[i];
+		if (!class) continue;
 		if (class->default_range != 0) {
 			rc = write_default_range_to_conf(out, pdb->p_class_val_to_name[i], class);
 			if (rc != 0) {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 19/36] libsepol: do not crash on user gaps
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (17 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 18/36] libsepol: do not crash on class gaps Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 20/36] libsepol: use correct size for initial string list Christian Göttsche
                       ` (17 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Handle gaps in the user table while printing a policy configuration.

    ==24424==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x0000004bdc55 bp 0x7ffc8790b810 sp 0x7ffc8790afb0 T0)
    ==24424==The signal is caused by a READ memory access.
    ==24424==Hint: address points to the zero page.
        #0 0x4bdc55 in __interceptor_strcmp (./out/binpolicy-fuzzer+0x4bdc55)
        #1 0x5ebdf6 in strs_cmp ./libsepol/src/kernel_to_common.c:253:9
        #2 0x505669 in __interceptor_qsort (./out/binpolicy-fuzzer+0x505669)
        #3 0x5ebd84 in strs_sort ./libsepol/src/kernel_to_common.c:261:2
        #4 0x564550 in write_user_decl_rules_to_conf ./libsepol/src/kernel_to_conf.c:2333:2
        #5 0x55b137 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3190:7
        #6 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #7 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #8 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #9 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #10 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #11 0x7f530128d7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #12 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_cil.c  | 1 +
 libsepol/src/kernel_to_conf.c | 1 +
 2 files changed, 2 insertions(+)

diff --git a/libsepol/src/kernel_to_cil.c b/libsepol/src/kernel_to_cil.c
index d9afdda6..26868f2d 100644
--- a/libsepol/src/kernel_to_cil.c
+++ b/libsepol/src/kernel_to_cil.c
@@ -2397,6 +2397,7 @@ static int write_user_decl_rules_to_cil(FILE *out, struct policydb *pdb)
 	}
 
 	for (i=0; i < pdb->p_users.nprim; i++) {
+		if (!pdb->p_user_val_to_name[i]) continue;
 		rc = strs_add(strs, pdb->p_user_val_to_name[i]);
 		if (rc != 0) {
 			goto exit;
diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index 92a342d1..b2ad4e02 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -2327,6 +2327,7 @@ static int write_user_decl_rules_to_conf(FILE *out, struct policydb *pdb)
 	}
 
 	for (i=0; i < pdb->p_users.nprim; i++) {
+		if (!pdb->p_user_val_to_name[i]) continue;
 		rc = strs_add(strs, pdb->p_user_val_to_name[i]);
 		if (rc != 0) {
 			goto exit;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 20/36] libsepol: use correct size for initial string list
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (18 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 19/36] libsepol: do not crash on user gaps Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 21/36] libsepol: do not create a string list with initial size zero Christian Göttsche
                       ` (16 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Use the number of categories not levels, which might be zero, for the
string list initial size of categories.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_conf.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libsepol/src/kernel_to_conf.c b/libsepol/src/kernel_to_conf.c
index b2ad4e02..09c08618 100644
--- a/libsepol/src/kernel_to_conf.c
+++ b/libsepol/src/kernel_to_conf.c
@@ -918,7 +918,7 @@ static int write_category_rules_to_conf(FILE *out, struct policydb *pdb)
 	unsigned i, j, num;
 	int rc = 0;
 
-	rc = strs_init(&strs, pdb->p_levels.nprim);
+	rc = strs_init(&strs, pdb->p_cats.nprim);
 	if (rc != 0) {
 		goto exit;
 	}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 21/36] libsepol: do not create a string list with initial size zero
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (19 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 20/36] libsepol: use correct size for initial string list Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 22/36] libsepol: split validation of datum array gaps and entries Christian Göttsche
                       ` (15 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Currently is it implementation defined, due to the size being passed to
calloc(3), whether the operations fails nor not.
Also strs_add() does not handle a size of zero, cause it just multiplies
the size by two.

Use a default size of 1 if 0 is passed and swap the calloc arguments for
consistency.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/kernel_to_common.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/libsepol/src/kernel_to_common.c b/libsepol/src/kernel_to_common.c
index 152f2816..9f5400c9 100644
--- a/libsepol/src/kernel_to_common.c
+++ b/libsepol/src/kernel_to_common.c
@@ -107,6 +107,10 @@ int strs_init(struct strs **strs, size_t size)
 {
 	struct strs *new;
 
+	if (size == 0) {
+		size = 1;
+	}
+
 	*strs = NULL;
 
 	new = malloc(sizeof(struct strs));
@@ -115,7 +119,7 @@ int strs_init(struct strs **strs, size_t size)
 		return -1;
 	}
 
-	new->list = calloc(sizeof(char *), size);
+	new->list = calloc(size, sizeof(char *));
 	if (!new->list) {
 		sepol_log_err("Out of memory");
 		free(new);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 22/36] libsepol: split validation of datum array gaps and entries
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (20 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 21/36] libsepol: do not create a string list with initial size zero Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 23/36] libsepol: validate MLS levels Christian Göttsche
                       ` (14 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Split the validation of array datums regarding their gaps and entries to
simplify further checking of common classes, booleans, levels and
categories.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 112 ++++++++++++++++++++-----------
 1 file changed, 73 insertions(+), 39 deletions(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 5804d247..d4dfab5c 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -6,11 +6,19 @@
 #include "debug.h"
 #include "policydb_validate.h"
 
+#define bool_xor(a, b) (!(a) != !(b))
+#define bool_xnor(a, b) !bool_xor(a, b)
+
 typedef struct validate {
 	uint32_t nprim;
 	ebitmap_t gaps;
 } validate_t;
 
+typedef struct map_arg {
+	validate_t *flavors;
+	sepol_handle_t *handle;
+	int mls;
+} map_arg_t;
 
 static int create_gap_ebitmap(char **val_to_name, uint32_t nprim, ebitmap_t *gaps)
 {
@@ -211,6 +219,13 @@ bad:
 	return -1;
 }
 
+static int validate_class_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_class_datum(margs->handle, d, margs->flavors);
+}
+
 static int validate_role_datum(sepol_handle_t *handle, role_datum_t *role, validate_t flavors[])
 {
 	if (validate_value(role->s.value, &flavors[SYM_ROLES]))
@@ -231,6 +246,13 @@ bad:
 	return -1;
 }
 
+static int validate_role_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_role_datum(margs->handle, d, margs->flavors);
+}
+
 static int validate_type_datum(sepol_handle_t *handle, type_datum_t *type, validate_t flavors[])
 {
 	if (validate_value(type->s.value, &flavors[SYM_TYPES]))
@@ -247,6 +269,13 @@ bad:
 	return -1;
 }
 
+static int validate_type_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_type_datum(margs->handle, d, margs->flavors);
+}
+
 static int validate_mls_semantic_cat(mls_semantic_cat_t *cat, validate_t *cats)
 {
 	for (; cat; cat = cat->next) {
@@ -310,32 +339,25 @@ bad:
 	return -1;
 }
 
-static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+static int validate_user_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_user_datum(margs->handle, d, margs->flavors);
+}
+
+static int validate_datum_array_gaps(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
 {
 	unsigned int i;
 
 	for (i = 0; i < p->p_classes.nprim; i++) {
-		if (p->class_val_to_struct[i]) {
-			if (ebitmap_get_bit(&flavors[SYM_CLASSES].gaps, i))
-				goto bad;
-			if (validate_class_datum(handle, p->class_val_to_struct[i], flavors))
-				goto bad;
-		} else {
-			if (!ebitmap_get_bit(&flavors[SYM_CLASSES].gaps, i))
-				goto bad;
-		}
+		if (bool_xnor(p->class_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_CLASSES].gaps, i)))
+			goto bad;
 	}
 
 	for (i = 0; i < p->p_roles.nprim; i++) {
-		if (p->role_val_to_struct[i]) {
-			if (ebitmap_get_bit(&flavors[SYM_ROLES].gaps, i))
-				goto bad;
-			if (validate_role_datum(handle, p->role_val_to_struct[i], flavors))
-				goto bad;
-		} else {
-			if (!ebitmap_get_bit(&flavors[SYM_ROLES].gaps, i))
-				goto bad;
-		}
+		if (bool_xnor(p->role_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_ROLES].gaps, i)))
+			goto bad;
 	}
 
 	/*
@@ -344,34 +366,43 @@ static int validate_datum_arrays(sepol_handle_t *handle, policydb_t *p, validate
 	 */
 	if (p->policyvers < POLICYDB_VERSION_AVTAB || p->policyvers > POLICYDB_VERSION_PERMISSIVE) {
 		for (i = 0; i < p->p_types.nprim; i++) {
-			if (p->type_val_to_struct[i]) {
-				if (ebitmap_get_bit(&flavors[SYM_TYPES].gaps, i))
-					goto bad;
-				if (validate_type_datum(handle, p->type_val_to_struct[i], flavors))
-					goto bad;
-			} else {
-				if (!ebitmap_get_bit(&flavors[SYM_TYPES].gaps, i))
-					goto bad;
-			}
+			if (bool_xnor(p->type_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_TYPES].gaps, i)))
+				goto bad;
 		}
 	}
 
 	for (i = 0; i < p->p_users.nprim; i++) {
-		if (p->user_val_to_struct[i]) {
-			if (ebitmap_get_bit(&flavors[SYM_USERS].gaps, i))
-				goto bad;
-			if (validate_user_datum(handle, p->user_val_to_struct[i], flavors))
-				goto bad;
-		} else {
-			if (!ebitmap_get_bit(&flavors[SYM_USERS].gaps, i))
-				goto bad;
-		}
+		if (bool_xnor(p->user_val_to_struct[i], ebitmap_get_bit(&flavors[SYM_USERS].gaps, i)))
+			goto bad;
 	}
 
 	return 0;
 
 bad:
-	ERR(handle, "Invalid datum arrays");
+	ERR(handle, "Invalid datum array gaps");
+	return -1;
+}
+
+static int validate_datum_array_entries(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	map_arg_t margs = { flavors, handle, p->mls };
+
+	if (hashtab_map(p->p_classes.table, validate_class_datum_wrapper, &margs))
+		goto bad;
+
+	if (hashtab_map(p->p_roles.table, validate_role_datum_wrapper, &margs))
+		goto bad;
+
+	if (hashtab_map(p->p_types.table, validate_type_datum_wrapper, &margs))
+		goto bad;
+
+	if (hashtab_map(p->p_users.table, validate_user_datum_wrapper, &margs))
+		goto bad;
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid datum array entries");
 	return -1;
 }
 
@@ -762,7 +793,10 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 	if (validate_scopes(handle, p->scope, p->global))
 		goto bad;
 
-	if (validate_datum_arrays(handle, p, flavors))
+	if (validate_datum_array_gaps(handle, p, flavors))
+		goto bad;
+
+	if (validate_datum_array_entries(handle, p, flavors))
 		goto bad;
 
 	validate_array_destroy(flavors);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 23/36] libsepol: validate MLS levels
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (21 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 22/36] libsepol: split validation of datum array gaps and entries Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 24/36] libsepol: validate expanded user range and level Christian Göttsche
                       ` (13 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Validate the level map of the policy to ensure no level refers to a non
existent category.

READ of size 8 at 0x602000000c58 thread T0
    #0 0x568d2c in cats_ebitmap_len ./libsepol/src/kernel_to_conf.c:1003:14
    #1 0x568d2c in cats_ebitmap_to_str ./libsepol/src/kernel_to_conf.c:1038:19
    #2 0x55e371 in write_level_rules_to_conf ./libsepol/src/kernel_to_conf.c:1106:11
    #3 0x55e371 in write_mls_rules_to_conf ./libsepol/src/kernel_to_conf.c:1140:7
    #4 0x55adb1 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3103:7
    #5 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
    #6 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
    #7 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
    #8 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
    #9 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
    #10 0x7f741d0d67ec in __libc_start_main csu/../csu/libc-start.c:332:16
    #11 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index d4dfab5c..03ab4445 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -319,6 +319,27 @@ bad:
 	return -1;
 }
 
+static int validate_mls_level(mls_level_t *level, validate_t *sens, validate_t *cats)
+{
+	if (validate_value(level->sens, sens))
+		goto bad;
+	if (validate_ebitmap(&level->cat, cats))
+		goto bad;
+
+	return 0;
+
+	bad:
+	return -1;
+}
+
+static int validate_level_datum(__attribute__ ((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	level_datum_t *level = d;
+	validate_t *flavors = args;
+
+	return validate_mls_level(level->level, &flavors[SYM_LEVELS], &flavors[SYM_CATS]);
+}
+
 static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, validate_t flavors[])
 {
 	if (validate_value(user->s.value, &flavors[SYM_USERS]))
@@ -399,6 +420,9 @@ static int validate_datum_array_entries(sepol_handle_t *handle, policydb_t *p, v
 	if (hashtab_map(p->p_users.table, validate_user_datum_wrapper, &margs))
 		goto bad;
 
+	if (p->mls && hashtab_map(p->p_levels.table, validate_level_datum, flavors))
+		goto bad;
+
 	return 0;
 
 bad:
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 24/36] libsepol: validate expanded user range and level
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (22 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 23/36] libsepol: validate MLS levels Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 25/36] libsepol: validate permission count of classes Christian Göttsche
                       ` (12 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Check those contains valid values.

    ==57532==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000001178 at pc 0x000000564c04 bp 0x7ffed7a5ad90 sp 0x7ffed7a5ad88
    READ of size 8 at 0x603000001178 thread T0
        #0 0x564c03 in level_to_str ./libsepol/src/kernel_to_conf.c:1901:19
        #1 0x564c03 in range_to_str ./libsepol/src/kernel_to_conf.c:1926:9
        #2 0x564c03 in write_user_decl_rules_to_conf ./libsepol/src/kernel_to_conf.c:2367:12
        #3 0x55b137 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3184:7
        #4 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #5 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #6 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #7 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #8 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #9 0x7f2c2e1a77ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #10 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 03ab4445..adaa3fb2 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -340,7 +340,20 @@ static int validate_level_datum(__attribute__ ((unused)) hashtab_key_t k, hashta
 	return validate_mls_level(level->level, &flavors[SYM_LEVELS], &flavors[SYM_CATS]);
 }
 
-static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, validate_t flavors[])
+static int validate_mls_range(mls_range_t *range, validate_t *sens, validate_t *cats)
+{
+	if (validate_mls_level(&range->level[0], sens, cats))
+		goto bad;
+	if (validate_mls_level(&range->level[1], sens, cats))
+		goto bad;
+
+	return 0;
+
+	bad:
+	return -1;
+}
+
+static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, validate_t flavors[], int mls)
 {
 	if (validate_value(user->s.value, &flavors[SYM_USERS]))
 		goto bad;
@@ -350,6 +363,10 @@ static int validate_user_datum(sepol_handle_t *handle, user_datum_t *user, valid
 		goto bad;
 	if (validate_mls_semantic_level(&user->dfltlevel, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
 		goto bad;
+	if (mls && validate_mls_range(&user->exp_range, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		goto bad;
+	if (mls && validate_mls_level(&user->exp_dfltlevel, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		goto bad;
 	if (user->bounds && validate_value(user->bounds, &flavors[SYM_USERS]))
 		goto bad;
 
@@ -364,7 +381,7 @@ static int validate_user_datum_wrapper(__attribute__((unused)) hashtab_key_t k,
 {
 	map_arg_t *margs = args;
 
-	return validate_user_datum(margs->handle, d, margs->flavors);
+	return validate_user_datum(margs->handle, d, margs->flavors, margs->mls);
 }
 
 static int validate_datum_array_gaps(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 25/36] libsepol: validate permission count of classes
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (23 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 24/36] libsepol: validate expanded user range and level Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 26/36] libsepol: resolve log message mismatch Christian Göttsche
                       ` (11 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Check a common class or a class together with its common class parent
does not have more than the supported 32 permissions.

    ==28413==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f74ec3341a3 bp 0x7ffd0b7e5030 sp 0x7ffd0b7e47e8 T0)
    ==28413==The signal is caused by a READ memory access.
    ==28413==Hint: address points to the zero page.
        #0 0x7f74ec3341a3  string/../sysdeps/x86_64/multiarch/../strchr.S:32
        #1 0x4bfc78 in strchr (./out/binpolicy-fuzzer+0x4bfc78)
        #2 0x55b7f2 in class_constraint_rules_to_strs ./libsepol/src/kernel_to_conf.c:288:7
        #3 0x55b7f2 in constraint_rules_to_strs ./libsepol/src/kernel_to_conf.c:364:9
        #4 0x55ac80 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3071:7
        #5 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #6 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #7 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #8 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #9 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #10 0x7f74ec2be7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #11 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   also check common classes
---
 libsepol/src/policydb_validate.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index adaa3fb2..e8d70585 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -211,6 +211,8 @@ static int validate_class_datum(sepol_handle_t *handle, class_datum_t *class, va
 		goto bad;
 	if (validate_constraint_nodes(handle, class->validatetrans, flavors))
 		goto bad;
+	if (class->permissions.nprim > PERM_SYMTAB_SIZE)
+		goto bad;
 
 	return 0;
 
@@ -226,6 +228,25 @@ static int validate_class_datum_wrapper(__attribute__((unused)) hashtab_key_t k,
 	return validate_class_datum(margs->handle, d, margs->flavors);
 }
 
+static int validate_common_datum(sepol_handle_t *handle, common_datum_t *common)
+{
+	if (common->permissions.nprim > PERM_SYMTAB_SIZE)
+		goto bad;
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid common class datum");
+	return -1;
+}
+
+static int validate_common_datum_wrapper(__attribute__((unused)) hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	map_arg_t *margs = args;
+
+	return validate_common_datum(margs->handle, d);
+}
+
 static int validate_role_datum(sepol_handle_t *handle, role_datum_t *role, validate_t flavors[])
 {
 	if (validate_value(role->s.value, &flavors[SYM_ROLES]))
@@ -425,6 +446,9 @@ static int validate_datum_array_entries(sepol_handle_t *handle, policydb_t *p, v
 {
 	map_arg_t margs = { flavors, handle, p->mls };
 
+	if (hashtab_map(p->p_commons.table, validate_common_datum_wrapper, &margs))
+		goto bad;
+
 	if (hashtab_map(p->p_classes.table, validate_class_datum_wrapper, &margs))
 		goto bad;
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 26/36] libsepol: resolve log message mismatch
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (24 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 25/36] libsepol: validate permission count of classes Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 27/36] libsepol: validate avtab and avrule types Christian Göttsche
                       ` (10 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index e8d70585..82193379 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -263,7 +263,7 @@ static int validate_role_datum(sepol_handle_t *handle, role_datum_t *role, valid
 	return 0;
 
 bad:
-	ERR(handle, "Invalid class datum");
+	ERR(handle, "Invalid role datum");
 	return -1;
 }
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 27/36] libsepol: validate avtab and avrule types
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (25 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 26/36] libsepol: resolve log message mismatch Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 28/36] libsepol: validate constraint expression operators and attributes Christian Göttsche
                       ` (9 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Check for invalid avtab or avrule types.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   also check avrule types
---
 libsepol/src/policydb_validate.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 82193379..5ef95c61 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -483,6 +483,20 @@ static int validate_avtab_key(avtab_key_t *key, validate_t flavors[])
 		goto bad;
 	if (validate_value(key->target_class, &flavors[SYM_CLASSES]))
 		goto bad;
+	switch (0xFFF & key->specified) {
+	case AVTAB_ALLOWED:
+	case AVTAB_AUDITALLOW:
+	case AVTAB_AUDITDENY:
+	case AVTAB_XPERMS_ALLOWED:
+	case AVTAB_XPERMS_AUDITALLOW:
+	case AVTAB_XPERMS_DONTAUDIT:
+	case AVTAB_TRANSITION:
+	case AVTAB_MEMBER:
+	case AVTAB_CHANGE:
+		break;
+	default:
+		goto bad;
+	}
 
 	return 0;
 
@@ -536,6 +550,23 @@ static int validate_avrules(sepol_handle_t *handle, avrule_t *avrule, validate_t
 			if (validate_value(class->tclass, &flavors[SYM_CLASSES]))
 				goto bad;
 		}
+		switch(avrule->specified) {
+		case AVRULE_ALLOWED:
+		case AVRULE_AUDITALLOW:
+		case AVRULE_AUDITDENY:
+		case AVRULE_DONTAUDIT:
+		case AVRULE_NEVERALLOW:
+		case AVRULE_TRANSITION:
+		case AVRULE_MEMBER:
+		case AVRULE_CHANGE:
+		case AVRULE_XPERMS_ALLOWED:
+		case AVRULE_XPERMS_AUDITALLOW:
+		case AVRULE_XPERMS_DONTAUDIT:
+		case AVRULE_XPERMS_NEVERALLOW:
+			break;
+		default:
+			goto bad;
+		}
 	}
 
 	return 0;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 28/36] libsepol: validate constraint expression operators and attributes
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (26 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 27/36] libsepol: validate avtab and avrule types Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 29/36] libsepol: validate type of avtab type rules Christian Göttsche
                       ` (8 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 43 ++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 5ef95c61..25c6f0db 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -193,6 +193,49 @@ static int validate_constraint_nodes(sepol_handle_t *handle, constraint_node_t *
 				if (validate_type_set(cexp->type_names, &flavors[SYM_TYPES]))
 					goto bad;
 			}
+
+			if (cexp->expr_type == CEXPR_ATTR || cexp->expr_type == CEXPR_NAMES) {
+				switch (cexp->op) {
+				case CEXPR_EQ:
+				case CEXPR_NEQ:
+				case CEXPR_DOM:
+				case CEXPR_DOMBY:
+				case CEXPR_INCOMP:
+					break;
+				default:
+					goto bad;
+				}
+
+				switch (cexp->attr) {
+				case CEXPR_USER:
+				case CEXPR_USER | CEXPR_TARGET:
+				case CEXPR_USER | CEXPR_XTARGET:
+				case CEXPR_ROLE:
+				case CEXPR_ROLE | CEXPR_TARGET:
+				case CEXPR_ROLE | CEXPR_XTARGET:
+				case CEXPR_TYPE:
+				case CEXPR_TYPE | CEXPR_TARGET:
+				case CEXPR_TYPE | CEXPR_XTARGET:
+				case CEXPR_L1L2:
+				case CEXPR_L1H2:
+				case CEXPR_H1L2:
+				case CEXPR_H1H2:
+				case CEXPR_L1H1:
+				case CEXPR_L2H2:
+					break;
+				default:
+					goto bad;
+				}
+			} else {
+				switch (cexp->expr_type) {
+				case CEXPR_NOT:
+				case CEXPR_AND:
+				case CEXPR_OR:
+					break;
+				default:
+					goto bad;
+				}
+			}
 		}
 	}
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 29/36] libsepol: validate type of avtab type rules
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (27 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 28/36] libsepol: validate constraint expression operators and attributes Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 30/36] libsepol: validate ocontexts Christian Göttsche
                       ` (7 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

    ==80903==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000005c0 at pc 0x0000005696c8 bp 0x7ffdb11ea560 sp 0x7ffdb11ea558
    READ of size 8 at 0x6020000005c0 thread T0
        #0 0x5696c7 in avtab_node_to_str ./libsepol/src/kernel_to_conf.c:1736:9
        #1 0x569013 in map_avtab_write_helper ./libsepol/src/kernel_to_conf.c:1767:10
        #2 0x5ab837 in avtab_map ./libsepol/src/avtab.c:347:10
        #3 0x561f9a in write_avtab_flavor_to_conf ./libsepol/src/kernel_to_conf.c:1798:7
        #4 0x561f9a in write_avtab_to_conf ./libsepol/src/kernel_to_conf.c:1819:8
        #5 0x55afba in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3159:7
        #6 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #7 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #8 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #9 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #10 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #11 0x7f97a83fd7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #12 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 25c6f0db..57eb2550 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -547,15 +547,22 @@ bad:
 	return -1;
 }
 
-static int validate_avtab_key_wrapper(avtab_key_t *k,  __attribute__ ((unused)) avtab_datum_t *d, void *args)
+static int validate_avtab_key_and_datum(avtab_key_t *k, avtab_datum_t *d, void *args)
 {
 	validate_t *flavors = (validate_t *)args;
-	return validate_avtab_key(k, flavors);
+
+	if (validate_avtab_key(k, flavors))
+		return -1;
+
+	if ((k->specified & AVTAB_TYPE) && validate_value(d->data, &flavors[SYM_TYPES]))
+		return -1;
+
+	return 0;
 }
 
 static int validate_avtab(sepol_handle_t *handle, avtab_t *avtab, validate_t flavors[])
 {
-	if (avtab_map(avtab, validate_avtab_key_wrapper, flavors)) {
+	if (avtab_map(avtab, validate_avtab_key_and_datum, flavors)) {
 		ERR(handle, "Invalid avtab");
 		return -1;
 	}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 30/36] libsepol: validate ocontexts
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (28 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 29/36] libsepol: validate type of avtab type rules Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 31/36] libsepol: validate genfs contexts Christian Göttsche
                       ` (6 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Check the literal contexts in ocontext statements are defined.

    ==91274==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f60b0afe8c6 bp 0x7ffd42edc990 sp 0x7ffd42edc148 T0)
    ==91274==The signal is caused by a READ memory access.
    ==91274==Hint: address points to the zero page.
        #0 0x7f60b0afe8c6  string/../sysdeps/x86_64/multiarch/../strlen.S:120
        #1 0x4bd128 in __interceptor_strlen (./out/binpolicy-fuzzer+0x4bd128)
        #2 0x5eb387 in create_str_helper ./libsepol/src/kernel_to_common.c:69:10
        #3 0x5eb11e in create_str ./libsepol/src/kernel_to_common.c:99:8
        #4 0x56ad7b in context_to_str ./libsepol/src/kernel_to_conf.c:2408:9
        #5 0x56a717 in write_sid_context_rules_to_conf ./libsepol/src/kernel_to_conf.c:2441:9
        #6 0x55b26c in write_selinux_isid_rules_to_conf ./libsepol/src/kernel_to_conf.c:2476:9
        #7 0x55b26c in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3206:8
        #8 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:38:9
        #9 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #10 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #11 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #12 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #13 0x7f60b0a887ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #14 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v3:
   only check FS and NETIF ocons in selinux policies (not xen)
v2:
   also check in base modules
---
 libsepol/src/policydb_validate.c | 46 ++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 57eb2550..bedf3b90 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -736,6 +736,49 @@ static int validate_filename_trans_hashtab(sepol_handle_t *handle, hashtab_t fil
 	return 0;
 }
 
+static int validate_context(context_struct_t *con, validate_t flavors[], int mls)
+{
+	if (validate_value(con->user, &flavors[SYM_USERS]))
+		return -1;
+	if (validate_value(con->role, &flavors[SYM_ROLES]))
+		return -1;
+	if (validate_value(con->type, &flavors[SYM_TYPES]))
+		return -1;
+	if (mls && validate_mls_range(&con->range, &flavors[SYM_LEVELS], &flavors[SYM_CATS]))
+		return -1;
+
+	return 0;
+}
+
+static int validate_ocontexts(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	ocontext_t *octx;
+	unsigned int i;
+
+	for (i = 0; i < OCON_NUM; i++) {
+		for (octx = p->ocontexts[i]; octx; octx = octx->next) {
+			if (validate_context(&octx->context[0], flavors, p->mls))
+				goto bad;
+
+			if (p->target_platform == SEPOL_TARGET_SELINUX) {
+				switch (i) {
+				case OCON_FS:
+				case OCON_NETIF:
+					if (validate_context(&octx->context[1], flavors, p->mls))
+						goto bad;
+					break;
+				}
+			}
+		}
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid ocontext");
+	return -1;
+}
+
 /*
  * Functions to validate a module policydb
  */
@@ -936,6 +979,9 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 			goto bad;
 	}
 
+	if (validate_ocontexts(handle, p, flavors))
+		goto bad;
+
 	if (validate_scopes(handle, p->scope, p->global))
 		goto bad;
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 31/36] libsepol: validate genfs contexts
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (29 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 30/36] libsepol: validate ocontexts Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 32/36] libsepol: validate permissive types Christian Göttsche
                       ` (5 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Check the literal contexts in a genfs statement are defined.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   also check in base modules
---
 libsepol/src/policydb_validate.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index bedf3b90..11f13d65 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -779,6 +779,25 @@ bad:
 	return -1;
 }
 
+static int validate_genfs(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	genfs_t *genfs;
+	ocontext_t *octx;
+
+	for (genfs = p->genfs; genfs; genfs = genfs->next) {
+		for (octx = genfs->head; octx; octx = octx->next) {
+			if (validate_context(&octx->context[0], flavors, p->mls))
+				goto bad;
+		}
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid genfs");
+	return -1;
+}
+
 /*
  * Functions to validate a module policydb
  */
@@ -982,6 +1001,9 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 	if (validate_ocontexts(handle, p, flavors))
 		goto bad;
 
+	if (validate_genfs(handle, p, flavors))
+		goto bad;
+
 	if (validate_scopes(handle, p->scope, p->global))
 		goto bad;
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 32/36] libsepol: validate permissive types
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (30 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 31/36] libsepol: validate genfs contexts Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 33/36] libsepol: validate policy properties Christian Göttsche
                       ` (4 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 11f13d65..d9968a8e 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -961,6 +961,23 @@ bad:
 	return -1;
 }
 
+static int validate_permissives(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
+{
+	ebitmap_node_t *node;
+	unsigned i;
+
+	ebitmap_for_each_positive_bit(&p->permissive_map, node, i) {
+		if (validate_value(i, &flavors[SYM_TYPES]))
+			goto bad;
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid permissive type");
+	return -1;
+}
+
 static void validate_array_destroy(validate_t flavors[])
 {
 	unsigned int i;
@@ -1013,6 +1030,9 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 	if (validate_datum_array_entries(handle, p, flavors))
 		goto bad;
 
+	if (validate_permissives(handle, p, flavors))
+		goto bad;
+
 	validate_array_destroy(flavors);
 
 	return 0;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 33/36] libsepol: validate policy properties
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (31 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 32/36] libsepol: validate permissive types Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 34/36] libsepol: validate categories Christian Göttsche
                       ` (3 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 51 ++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index d9968a8e..fc0b26a3 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -978,6 +978,54 @@ bad:
 	return -1;
 }
 
+static int validate_properties(sepol_handle_t *handle, policydb_t *p)
+{
+	switch (p->policy_type) {
+	case POLICY_KERN:
+		if (p->policyvers < POLICYDB_VERSION_MIN || p->policyvers > POLICYDB_VERSION_MAX)
+			goto bad;
+		break;
+	case POLICY_BASE:
+	case POLICY_MOD:
+		if (p->policyvers < MOD_POLICYDB_VERSION_MIN || p->policyvers > MOD_POLICYDB_VERSION_MAX)
+			goto bad;
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (p->target_platform) {
+	case SEPOL_TARGET_SELINUX:
+	case SEPOL_TARGET_XEN:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (p->mls) {
+	case 0:
+	case 1:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (p->handle_unknown) {
+	case SEPOL_DENY_UNKNOWN:
+	case SEPOL_REJECT_UNKNOWN:
+	case SEPOL_ALLOW_UNKNOWN:
+		break;
+	default:
+		goto bad;
+	}
+
+	return 0;
+
+bad:
+	ERR(handle, "Invalid policy property");
+	return -1;
+}
+
 static void validate_array_destroy(validate_t flavors[])
 {
 	unsigned int i;
@@ -997,6 +1045,9 @@ int validate_policydb(sepol_handle_t *handle, policydb_t *p)
 	if (validate_array_init(p, flavors))
 		goto bad;
 
+	if (validate_properties(handle, p))
+		goto bad;
+
 	if (p->policy_type == POLICY_KERN) {
 		if (validate_avtab(handle, &p->te_avtab, flavors))
 			goto bad;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 34/36] libsepol: validate categories
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (32 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 33/36] libsepol: validate policy properties Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 35/36] libsepol: validate fsuse types Christian Göttsche
                       ` (2 subsequent siblings)
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Check all categories have valid values, especially important for
aliases.

        ==7888==ERROR: AddressSanitizer: SEGV on unknown address 0x602000400710 (pc 0x00000055debc bp 0x7ffe0ff2a9d0 sp 0x7ffe0ff2a8e0 T0)
        ==7888==The signal is caused by a READ memory access.
        #0 0x55debc in write_category_rules_to_conf ./libsepol/src/kernel_to_conf.c:946:9
        #1 0x55debc in write_mls_rules_to_conf ./libsepol/src/kernel_to_conf.c:1137:7
        #2 0x55adb1 in sepol_kernel_policydb_to_conf ./libsepol/src/kernel_to_conf.c:3106:7
        #3 0x55a34f in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:37:9
        #4 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
        #5 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
        #6 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
        #7 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
        #8 0x7fe80ccaf7ec in __libc_start_main csu/../csu/libc-start.c:332:16
        #9 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
 libsepol/src/policydb_validate.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index fc0b26a3..2f30a3ad 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -485,6 +485,14 @@ bad:
 	return -1;
 }
 
+static int validate_datum(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+	symtab_datum_t *s = d;
+	uint32_t *nprim = (uint32_t *)args;
+
+	return !value_isvalid(s->value, *nprim);
+}
+
 static int validate_datum_array_entries(sepol_handle_t *handle, policydb_t *p, validate_t flavors[])
 {
 	map_arg_t margs = { flavors, handle, p->mls };
@@ -507,6 +515,9 @@ static int validate_datum_array_entries(sepol_handle_t *handle, policydb_t *p, v
 	if (p->mls && hashtab_map(p->p_levels.table, validate_level_datum, flavors))
 		goto bad;
 
+	if (hashtab_map(p->p_cats.table, validate_datum, &flavors[SYM_CATS]))
+		goto bad;
+
 	return 0;
 
 bad:
@@ -905,14 +916,6 @@ bad:
 	return -1;
 }
 
-static int validate_datum(__attribute__ ((unused))hashtab_key_t k, hashtab_datum_t d, void *args)
-{
-	symtab_datum_t *s = d;
-	uint32_t *nprim = (uint32_t *)args;
-
-	return !value_isvalid(s->value, *nprim);
-}
-
 static int validate_symtabs(sepol_handle_t *handle, symtab_t symtabs[], validate_t flavors[])
 {
 	unsigned int i;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 35/36] libsepol: validate fsuse types
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (33 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 34/36] libsepol: validate categories Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-09 16:49     ` [PATCH v3 36/36] libsepol: validate class default targets Christian Göttsche
  2021-12-15 17:41     ` [PATCH v3 00/36] libsepol: add fuzzer for reading binary policies James Carter
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Check the fsuse type is valid, e.g. of type xattr, trans or task.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   do not reject in binary reading, but check at validation step
---
 libsepol/src/policydb_validate.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index 2f30a3ad..b2d0e5e5 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -2,6 +2,7 @@
 #include <sepol/policydb/conditional.h>
 #include <sepol/policydb/ebitmap.h>
 #include <sepol/policydb/policydb.h>
+#include <sepol/policydb/services.h>
 
 #include "debug.h"
 #include "policydb_validate.h"
@@ -778,6 +779,15 @@ static int validate_ocontexts(sepol_handle_t *handle, policydb_t *p, validate_t
 					if (validate_context(&octx->context[1], flavors, p->mls))
 						goto bad;
 					break;
+				case OCON_FSUSE:
+					switch (octx->v.behavior) {
+					case SECURITY_FS_USE_XATTR:
+					case SECURITY_FS_USE_TRANS:
+					case SECURITY_FS_USE_TASK:
+						break;
+					default:
+						goto bad;
+					}
 				}
 			}
 		}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* [PATCH v3 36/36] libsepol: validate class default targets
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (34 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 35/36] libsepol: validate fsuse types Christian Göttsche
@ 2021-12-09 16:49     ` Christian Göttsche
  2021-12-15 17:41     ` [PATCH v3 00/36] libsepol: add fuzzer for reading binary policies James Carter
  36 siblings, 0 replies; 135+ messages in thread
From: Christian Göttsche @ 2021-12-09 16:49 UTC (permalink / raw)
  To: selinux

Check the class default targets are valid values, e.g. source or target
for user, role and type.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>

---
v2:
   do not reject in binary reading, but check at validation step
---
 libsepol/src/policydb_validate.c | 41 ++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/libsepol/src/policydb_validate.c b/libsepol/src/policydb_validate.c
index b2d0e5e5..0650f4d1 100644
--- a/libsepol/src/policydb_validate.c
+++ b/libsepol/src/policydb_validate.c
@@ -258,6 +258,47 @@ static int validate_class_datum(sepol_handle_t *handle, class_datum_t *class, va
 	if (class->permissions.nprim > PERM_SYMTAB_SIZE)
 		goto bad;
 
+	switch (class->default_user) {
+	case 0:
+	case DEFAULT_SOURCE:
+	case DEFAULT_TARGET:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (class->default_role) {
+	case 0:
+	case DEFAULT_SOURCE:
+	case DEFAULT_TARGET:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (class->default_type) {
+	case 0:
+	case DEFAULT_SOURCE:
+	case DEFAULT_TARGET:
+		break;
+	default:
+		goto bad;
+	}
+
+	switch (class->default_range) {
+	case 0:
+	case DEFAULT_SOURCE_LOW:
+	case DEFAULT_SOURCE_HIGH:
+	case DEFAULT_SOURCE_LOW_HIGH:
+	case DEFAULT_TARGET_LOW:
+	case DEFAULT_TARGET_HIGH:
+	case DEFAULT_TARGET_LOW_HIGH:
+	case DEFAULT_GLBLUB:
+		break;
+	default:
+		goto bad;
+	}
+
 	return 0;
 
 bad:
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 135+ messages in thread

* Re: [PATCH v3 11/36] libsepol: enforce avtab item limit
  2021-12-09 16:49     ` [PATCH v3 11/36] libsepol: enforce avtab item limit Christian Göttsche
@ 2021-12-15 17:39       ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-12-15 17:39 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Thu, Dec 9, 2021 at 2:07 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Check the current item count does not exceed the maximum allowed to
> avoid stack overflows.
>
>     ==33660==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fa64b8fc070 at pc 0x0000005acba0 bp 0x7ffc1f0b2870 sp 0x7ffc1f0b2868
>     READ of size 4 at 0x7fa64b8fc070 thread T0
>         #0 0x5acb9f in avtab_read_item ./libsepol/src/avtab.c:507:18
>         #1 0x5acec4 in avtab_read ./libsepol/src/avtab.c:611:8
>         #2 0x576ae3 in policydb_read ./libsepol/src/policydb.c:4433:7
>         #3 0x55a1fe in LLVMFuzzerTestOneInput ./libsepol/fuzz/binpolicy-fuzzer.c:24:6
>         #4 0x45aed3 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) fuzzer.o
>         #5 0x446a12 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) fuzzer.o
>         #6 0x44c93b in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) fuzzer.o
>         #7 0x475dd2 in main (./out/binpolicy-fuzzer+0x475dd2)
>         #8 0x7fa64cc867ec in __libc_start_main csu/../csu/libc-start.c:332:16
>         #9 0x423689 in _start (./out/binpolicy-fuzzer+0x423689)
>
>     Address 0x7fa64b8fc070 is located in stack of thread T0 at offset 112 in frame
>         #0 0x5aabdf in avtab_read_item ./libsepol/src/avtab.c:437
>
>       This frame has 6 object(s):
>         [32, 33) 'buf8' (line 438)
>         [48, 56) 'buf16' (line 439)
>         [80, 112) 'buf32' (line 440) <== Memory access at offset 112 overflows this variable
>         [144, 152) 'key' (line 441)
>         [176, 192) 'datum' (line 442)
>         [208, 244) 'xperms' (line 443)
>     HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
>           (longjmp and C++ exceptions *are* supported)
>     SUMMARY: AddressSanitizer: stack-buffer-overflow ./libsepol/src/avtab.c:507:18 in avtab_read_item
>     Shadow bytes around the buggy address:
>       0x0ff5497177b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>       0x0ff5497177c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>       0x0ff5497177d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>       0x0ff5497177e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>       0x0ff5497177f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>     =>0x0ff549717800: f1 f1 f1 f1 01 f2 00 f2 f2 f2 00 00 00 00[f2]f2
>       0x0ff549717810: f2 f2 00 f2 f2 f2 00 00 f2 f2 00 00 00 00 04 f3
>       0x0ff549717820: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
>       0x0ff549717830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>       0x0ff549717840: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>       0x0ff549717850: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>     Shadow byte legend (one shadow byte represents 8 application bytes):
>       Addressable:           00
>       Partially addressable: 01 02 03 04 05 06 07
>       Heap left redzone:       fa
>       Freed heap region:       fd
>       Stack left redzone:      f1
>       Stack mid redzone:       f2
>       Stack right redzone:     f3
>       Stack after return:      f5
>       Stack use after scope:   f8
>       Global redzone:          f9
>       Global init order:       f6
>       Poisoned by user:        f7
>       Container overflow:      fc
>       Array cookie:            ac
>       Intra object redzone:    bb
>       ASan internal:           fe
>       Left alloca redzone:     ca
>       Right alloca redzone:    cb
>     ==33660==ABORTING
>
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
> v3:
>    take zero based numbering of variable items into account
> ---
>  libsepol/src/avtab.c | 6 ++++++
>  1 file changed, 6 insertions(+)
>
> diff --git a/libsepol/src/avtab.c b/libsepol/src/avtab.c
> index 46e1e75d..e9b17664 100644
> --- a/libsepol/src/avtab.c
> +++ b/libsepol/src/avtab.c
> @@ -503,6 +503,12 @@ int avtab_read_item(struct policy_file *fp, uint32_t vers, avtab_t * a,
>
>                 for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
>                         if (val & spec_order[i]) {
> +                               if (items >= items2) {
> +                                       ERR(fp->handle,
> +                                           "entry has too many items (%d/%d)",
> +                                           items + /* zero based numbered */ 1, items2);

I do not like comments in the middle of an expression. I will fix this
when I apply the series.
Jim


> +                                       return -1;
> +                               }
>                                 key.specified = spec_order[i] | enabled;
>                                 datum.data = le32_to_cpu(buf32[items++]);
>                                 rc = insertf(a, &key, &datum, p);
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [PATCH v3 00/36] libsepol: add fuzzer for reading binary policies
  2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
                       ` (35 preceding siblings ...)
  2021-12-09 16:49     ` [PATCH v3 36/36] libsepol: validate class default targets Christian Göttsche
@ 2021-12-15 17:41     ` James Carter
  2021-12-17 13:59       ` James Carter
  36 siblings, 1 reply; 135+ messages in thread
From: James Carter @ 2021-12-15 17:41 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Thu, Dec 9, 2021 at 2:07 PM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Add a libfuzz[1] based fuzzer testing the reading and parsing of binary policy
> files. This fuzzer will be run within the OSS-Fuzz service.
>
> Handle and reject a variety of edge cases causing crashes or resource leaks.
>
> The fifth patch ("libsepol/fuzz: limit element sizes for fuzzing") limits, to
> avoid oom reports from the fuzzer, caused by huge memory allocations, all
> identifiers to a length of 2^16 for the fuzzer build only.
> A potential limit for the release build needs further discussion.
>
> [1]: https://llvm.org/docs/LibFuzzer.html
>
> v3:
>   - Drop RFC status
>   - [10] libsepol: add checks for read sizes
>     use PERM_SYMTAB_SIZE instead of bare 32 as limit
>   - [11] libsepol: enforce avtab item limit
>     take zero based numbering of variable items into account
>   - [30] libsepol: validate ocontexts
>     only check FS and NETIF ocons in selinux policies (not xen)
>
> v2:
>   - reorder patches
>     1. oss-fuzz related
>     2. libsepol parsing and other crashesand UB
>     3. enhance policy validation
>   - misc changes based on review by James Carter
>
>
> Christian Göttsche (36):
>   cifuzz: enable report-unreproducible-crashes
>   cifuzz: use the default runtime of 600 seconds
>   libsepol/fuzz: silence secilc-fuzzer
>   libsepol: add libfuzz based fuzzer for reading binary policies
>   libsepol/fuzz: limit element sizes for fuzzing
>   libsepol: use logging framework in conditional.c
>   libsepol: use logging framework in ebitmap.c
>   libsepol: use mallocarray wrapper to avoid overflows
>   libsepol: use reallocarray wrapper to avoid overflows
>   libsepol: add checks for read sizes
>   libsepol: enforce avtab item limit
>   libsepol: clean memory on conditional insertion failure
>   libsepol: reject abnormal huge sid ids
>   libsepol: reject invalid filetrans source type
>   libsepol: zero member before potential dereference
>   libsepol: use size_t for indexes in strs helpers
>   libsepol: do not underflow on short format arguments
>   libsepol: do not crash on class gaps
>   libsepol: do not crash on user gaps
>   libsepol: use correct size for initial string list
>   libsepol: do not create a string list with initial size zero
>   libsepol: split validation of datum array gaps and entries
>   libsepol: validate MLS levels
>   libsepol: validate expanded user range and level
>   libsepol: validate permission count of classes
>   libsepol: resolve log message mismatch
>   libsepol: validate avtab and avrule types
>   libsepol: validate constraint expression operators and attributes
>   libsepol: validate type of avtab type rules
>   libsepol: validate ocontexts
>   libsepol: validate genfs contexts
>   libsepol: validate permissive types
>   libsepol: validate policy properties
>   libsepol: validate categories
>   libsepol: validate fsuse types
>   libsepol: validate class default targets
>
>  .github/workflows/cifuzz.yml     |   3 +-
>  libsepol/fuzz/binpolicy-fuzzer.c |  63 ++++
>  libsepol/fuzz/policy.bin         | Bin 0 -> 1552 bytes
>  libsepol/fuzz/secilc-fuzzer.c    |   5 +
>  libsepol/src/Makefile            |   6 +
>  libsepol/src/avtab.c             |   6 +
>  libsepol/src/conditional.c       |  53 ++--
>  libsepol/src/ebitmap.c           |  27 +-
>  libsepol/src/expand.c            |   4 +-
>  libsepol/src/hashtab.c           |   4 +-
>  libsepol/src/kernel_to_cil.c     |  10 +
>  libsepol/src/kernel_to_common.c  |  23 +-
>  libsepol/src/kernel_to_common.h  |   4 +-
>  libsepol/src/kernel_to_conf.c    |  13 +-
>  libsepol/src/link.c              |   3 +-
>  libsepol/src/module.c            |   4 +-
>  libsepol/src/module_to_cil.c     |  13 +-
>  libsepol/src/optimize.c          |  11 +-
>  libsepol/src/policydb.c          |  27 +-
>  libsepol/src/policydb_validate.c | 477 +++++++++++++++++++++++++++----
>  libsepol/src/private.h           |  27 +-
>  libsepol/src/services.c          |  12 +-
>  libsepol/src/sidtab.c            |   3 +-
>  libsepol/src/user_record.c       |   8 +-
>  libsepol/src/users.c             |  12 +-
>  libsepol/src/util.c              |  11 +-
>  libsepol/src/write.c             |   2 +-
>  scripts/oss-fuzz.sh              |  17 +-
>  28 files changed, 686 insertions(+), 162 deletions(-)
>  create mode 100644 libsepol/fuzz/binpolicy-fuzzer.c
>  create mode 100644 libsepol/fuzz/policy.bin
>
> --
> 2.34.1
>

For the series:
Acked-by: James Carter <jwcart2@gmail.com>

As I noted, I will move the comment out of the expression when I apply patch 11.

Thanks,
Jim

^ permalink raw reply	[flat|nested] 135+ messages in thread

* Re: [PATCH v3 00/36] libsepol: add fuzzer for reading binary policies
  2021-12-15 17:41     ` [PATCH v3 00/36] libsepol: add fuzzer for reading binary policies James Carter
@ 2021-12-17 13:59       ` James Carter
  0 siblings, 0 replies; 135+ messages in thread
From: James Carter @ 2021-12-17 13:59 UTC (permalink / raw)
  To: Christian Göttsche; +Cc: SElinux list

On Wed, Dec 15, 2021 at 12:41 PM James Carter <jwcart2@gmail.com> wrote:
>
> On Thu, Dec 9, 2021 at 2:07 PM Christian Göttsche
> <cgzones@googlemail.com> wrote:
> >
> > Add a libfuzz[1] based fuzzer testing the reading and parsing of binary policy
> > files. This fuzzer will be run within the OSS-Fuzz service.
> >
> > Handle and reject a variety of edge cases causing crashes or resource leaks.
> >
> > The fifth patch ("libsepol/fuzz: limit element sizes for fuzzing") limits, to
> > avoid oom reports from the fuzzer, caused by huge memory allocations, all
> > identifiers to a length of 2^16 for the fuzzer build only.
> > A potential limit for the release build needs further discussion.
> >
> > [1]: https://llvm.org/docs/LibFuzzer.html
> >
> > v3:
> >   - Drop RFC status
> >   - [10] libsepol: add checks for read sizes
> >     use PERM_SYMTAB_SIZE instead of bare 32 as limit
> >   - [11] libsepol: enforce avtab item limit
> >     take zero based numbering of variable items into account
> >   - [30] libsepol: validate ocontexts
> >     only check FS and NETIF ocons in selinux policies (not xen)
> >
> > v2:
> >   - reorder patches
> >     1. oss-fuzz related
> >     2. libsepol parsing and other crashesand UB
> >     3. enhance policy validation
> >   - misc changes based on review by James Carter
> >
> >
> > Christian Göttsche (36):
> >   cifuzz: enable report-unreproducible-crashes
> >   cifuzz: use the default runtime of 600 seconds
> >   libsepol/fuzz: silence secilc-fuzzer
> >   libsepol: add libfuzz based fuzzer for reading binary policies
> >   libsepol/fuzz: limit element sizes for fuzzing
> >   libsepol: use logging framework in conditional.c
> >   libsepol: use logging framework in ebitmap.c
> >   libsepol: use mallocarray wrapper to avoid overflows
> >   libsepol: use reallocarray wrapper to avoid overflows
> >   libsepol: add checks for read sizes
> >   libsepol: enforce avtab item limit
> >   libsepol: clean memory on conditional insertion failure
> >   libsepol: reject abnormal huge sid ids
> >   libsepol: reject invalid filetrans source type
> >   libsepol: zero member before potential dereference
> >   libsepol: use size_t for indexes in strs helpers
> >   libsepol: do not underflow on short format arguments
> >   libsepol: do not crash on class gaps
> >   libsepol: do not crash on user gaps
> >   libsepol: use correct size for initial string list
> >   libsepol: do not create a string list with initial size zero
> >   libsepol: split validation of datum array gaps and entries
> >   libsepol: validate MLS levels
> >   libsepol: validate expanded user range and level
> >   libsepol: validate permission count of classes
> >   libsepol: resolve log message mismatch
> >   libsepol: validate avtab and avrule types
> >   libsepol: validate constraint expression operators and attributes
> >   libsepol: validate type of avtab type rules
> >   libsepol: validate ocontexts
> >   libsepol: validate genfs contexts
> >   libsepol: validate permissive types
> >   libsepol: validate policy properties
> >   libsepol: validate categories
> >   libsepol: validate fsuse types
> >   libsepol: validate class default targets
> >
> >  .github/workflows/cifuzz.yml     |   3 +-
> >  libsepol/fuzz/binpolicy-fuzzer.c |  63 ++++
> >  libsepol/fuzz/policy.bin         | Bin 0 -> 1552 bytes
> >  libsepol/fuzz/secilc-fuzzer.c    |   5 +
> >  libsepol/src/Makefile            |   6 +
> >  libsepol/src/avtab.c             |   6 +
> >  libsepol/src/conditional.c       |  53 ++--
> >  libsepol/src/ebitmap.c           |  27 +-
> >  libsepol/src/expand.c            |   4 +-
> >  libsepol/src/hashtab.c           |   4 +-
> >  libsepol/src/kernel_to_cil.c     |  10 +
> >  libsepol/src/kernel_to_common.c  |  23 +-
> >  libsepol/src/kernel_to_common.h  |   4 +-
> >  libsepol/src/kernel_to_conf.c    |  13 +-
> >  libsepol/src/link.c              |   3 +-
> >  libsepol/src/module.c            |   4 +-
> >  libsepol/src/module_to_cil.c     |  13 +-
> >  libsepol/src/optimize.c          |  11 +-
> >  libsepol/src/policydb.c          |  27 +-
> >  libsepol/src/policydb_validate.c | 477 +++++++++++++++++++++++++++----
> >  libsepol/src/private.h           |  27 +-
> >  libsepol/src/services.c          |  12 +-
> >  libsepol/src/sidtab.c            |   3 +-
> >  libsepol/src/user_record.c       |   8 +-
> >  libsepol/src/users.c             |  12 +-
> >  libsepol/src/util.c              |  11 +-
> >  libsepol/src/write.c             |   2 +-
> >  scripts/oss-fuzz.sh              |  17 +-
> >  28 files changed, 686 insertions(+), 162 deletions(-)
> >  create mode 100644 libsepol/fuzz/binpolicy-fuzzer.c
> >  create mode 100644 libsepol/fuzz/policy.bin
> >
> > --
> > 2.34.1
> >
>
> For the series:
> Acked-by: James Carter <jwcart2@gmail.com>
>
> As I noted, I will move the comment out of the expression when I apply patch 11.
>
> Thanks,
> Jim

This series has been merged.
Thanks,
Jim

^ permalink raw reply	[flat|nested] 135+ messages in thread

end of thread, other threads:[~2021-12-17 14:00 UTC | newest]

Thread overview: 135+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-11 16:24 [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies Christian Göttsche
2021-10-11 16:24 ` [RFC PATCH 01/35] cifuzz: enable report-unreproducible-crashes Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 02/35] cifuzz: use the default runtime of 600 seconds Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 03/35] libsepol/fuzz: silence secilc-fuzzer Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 04/35] libsepol: add libfuzz based fuzzer for reading binary policies Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 05/35] libsepol/fuzz: limit element sizes for fuzzing Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 06/35] libsepol: use logging framework in conditional.c Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 07/35] libsepol: use logging framework in ebitmap.c Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 08/35] libsepol: use mallocarray wrapper to avoid overflows Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 09/35] libsepol: use reallocarray " Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 10/35] libsepol: add checks for read sizes Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 11/35] libsepol: enforce avtab item limit Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 12/35] libsepol: clean memory on conditional read failure Christian Göttsche
2021-10-13 14:10   ` James Carter
2021-10-11 16:25 ` [RFC PATCH 13/35] libsepol: validate MLS levels Christian Göttsche
2021-10-13 15:38   ` James Carter
2021-10-11 16:25 ` [RFC PATCH 14/35] libsepol: reject invalid fsuse types Christian Göttsche
2021-10-18 19:57   ` James Carter
2021-10-11 16:25 ` [RFC PATCH 15/35] libsepol: reject invalid default targets Christian Göttsche
2021-10-18 19:58   ` James Carter
2021-10-11 16:25 ` [RFC PATCH 16/35] libsepol: validate expanded user range and level Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 17/35] libsepol: validate types Christian Göttsche
2021-10-13 15:39   ` James Carter
2021-10-11 16:25 ` [RFC PATCH 18/35] libsepol: use size_t for indexes in strs helpers Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 19/35] libsepol: reject abnormal huge sid ids Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 20/35] libsepol: do not crash on class gaps Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 21/35] libsepol: do not crash on user gaps Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 22/35] libsepol: validate permission count of classes Christian Göttsche
2021-10-13 15:41   ` James Carter
2021-10-11 16:25 ` [RFC PATCH 23/35] libsepol: resolve log message mismatch Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 24/35] libsepol: zero member before potential dereference Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 25/35] libsepol: validate avtab types Christian Göttsche
2021-10-18 19:54   ` James Carter
2021-10-11 16:25 ` [RFC PATCH 26/35] libsepol: validate constraint expression operators and attributes Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 27/35] libsepol: validate type of avtab type rules Christian Göttsche
2021-10-13 15:44   ` James Carter
2021-10-11 16:25 ` [RFC PATCH 28/35] libsepol: validate ocontexts Christian Göttsche
2021-10-14 14:10   ` James Carter
2021-10-11 16:25 ` [RFC PATCH 29/35] libsepol: validate genfs contexts Christian Göttsche
2021-10-14 14:10   ` James Carter
2021-10-11 16:25 ` [RFC PATCH 30/35] libsepol: validate permissive types Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 31/35] libsepol: validate policy properties Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 32/35] libsepol: do not underflow on short format arguments Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 33/35] libsepol: validate categories Christian Göttsche
2021-10-13 15:40   ` James Carter
2021-10-11 16:25 ` [RFC PATCH 34/35] libsepol: use correct size for initial string list Christian Göttsche
2021-10-11 16:25 ` [RFC PATCH 35/35] libsepol: do not create a string list with initial size zero Christian Göttsche
2021-10-13 14:07 ` [RFC PATCH 00/35] libsepol: add fuzzer for reading binary policies James Carter
2021-11-05 15:45 ` [RFC PATCH v2 00/36] " Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 01/36] cifuzz: enable report-unreproducible-crashes Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 02/36] cifuzz: use the default runtime of 600 seconds Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 03/36] libsepol/fuzz: silence secilc-fuzzer Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 04/36] libsepol: add libfuzz based fuzzer for reading binary policies Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 05/36] libsepol/fuzz: limit element sizes for fuzzing Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 06/36] libsepol: use logging framework in conditional.c Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 07/36] libsepol: use logging framework in ebitmap.c Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 08/36] libsepol: use mallocarray wrapper to avoid overflows Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 09/36] libsepol: use reallocarray " Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 10/36] libsepol: add checks for read sizes Christian Göttsche
2021-11-09 18:46     ` James Carter
2021-11-09 18:58       ` Christian Göttsche
2021-11-09 19:17         ` James Carter
2021-11-05 15:45   ` [RFC PATCH v2 11/36] libsepol: enforce avtab item limit Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 12/36] libsepol: clean memory on conditional insertion failure Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 13/36] libsepol: reject abnormal huge sid ids Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 14/36] libsepol: reject invalid filetrans source type Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 15/36] libsepol: zero member before potential dereference Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 16/36] libsepol: use size_t for indexes in strs helpers Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 17/36] libsepol: do not underflow on short format arguments Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 18/36] libsepol: do not crash on class gaps Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 19/36] libsepol: do not crash on user gaps Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 20/36] libsepol: use correct size for initial string list Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 21/36] libsepol: do not create a string list with initial size zero Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 22/36] libsepol: split validation of datum array gaps and entries Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 23/36] libsepol: validate MLS levels Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 24/36] libsepol: validate expanded user range and level Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 25/36] libsepol: validate permission count of classes Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 26/36] libsepol: resolve log message mismatch Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 27/36] libsepol: validate avtab and avrule types Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 28/36] libsepol: validate constraint expression operators and attributes Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 29/36] libsepol: validate type of avtab type rules Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 30/36] libsepol: validate ocontexts Christian Göttsche
2021-11-09 19:04     ` James Carter
2021-11-05 15:45   ` [RFC PATCH v2 31/36] libsepol: validate genfs contexts Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 32/36] libsepol: validate permissive types Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 33/36] libsepol: validate policy properties Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 34/36] libsepol: validate categories Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 35/36] libsepol: validate fsuse types Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 36/36] libsepol: validate class default targets Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 37/40] [WIP] libsepol: export policydb_validate Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 38/40] [WIP] checkpolicy: validate generated policies Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 39/40] [CROSS-PATCH] libsepol: avoid passing NULL pointer to memcpy Christian Göttsche
2021-11-05 15:45   ` [RFC PATCH v2 40/40] [CROSS-PATCH] libsepol: do not pass NULL " Christian Göttsche
2021-11-09 18:42   ` [RFC PATCH v2 00/36] libsepol: add fuzzer for reading binary policies James Carter
2021-11-09 18:43     ` James Carter
2021-12-09 16:48   ` [PATCH v3 " Christian Göttsche
2021-12-09 16:48     ` [PATCH v3 01/36] cifuzz: enable report-unreproducible-crashes Christian Göttsche
2021-12-09 16:48     ` [PATCH v3 02/36] cifuzz: use the default runtime of 600 seconds Christian Göttsche
2021-12-09 16:48     ` [PATCH v3 03/36] libsepol/fuzz: silence secilc-fuzzer Christian Göttsche
2021-12-09 16:48     ` [PATCH v3 04/36] libsepol: add libfuzz based fuzzer for reading binary policies Christian Göttsche
2021-12-09 16:48     ` [PATCH v3 05/36] libsepol/fuzz: limit element sizes for fuzzing Christian Göttsche
2021-12-09 16:48     ` [PATCH v3 06/36] libsepol: use logging framework in conditional.c Christian Göttsche
2021-12-09 16:48     ` [PATCH v3 07/36] libsepol: use logging framework in ebitmap.c Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 08/36] libsepol: use mallocarray wrapper to avoid overflows Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 09/36] libsepol: use reallocarray " Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 10/36] libsepol: add checks for read sizes Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 11/36] libsepol: enforce avtab item limit Christian Göttsche
2021-12-15 17:39       ` James Carter
2021-12-09 16:49     ` [PATCH v3 12/36] libsepol: clean memory on conditional insertion failure Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 13/36] libsepol: reject abnormal huge sid ids Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 14/36] libsepol: reject invalid filetrans source type Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 15/36] libsepol: zero member before potential dereference Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 16/36] libsepol: use size_t for indexes in strs helpers Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 17/36] libsepol: do not underflow on short format arguments Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 18/36] libsepol: do not crash on class gaps Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 19/36] libsepol: do not crash on user gaps Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 20/36] libsepol: use correct size for initial string list Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 21/36] libsepol: do not create a string list with initial size zero Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 22/36] libsepol: split validation of datum array gaps and entries Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 23/36] libsepol: validate MLS levels Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 24/36] libsepol: validate expanded user range and level Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 25/36] libsepol: validate permission count of classes Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 26/36] libsepol: resolve log message mismatch Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 27/36] libsepol: validate avtab and avrule types Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 28/36] libsepol: validate constraint expression operators and attributes Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 29/36] libsepol: validate type of avtab type rules Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 30/36] libsepol: validate ocontexts Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 31/36] libsepol: validate genfs contexts Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 32/36] libsepol: validate permissive types Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 33/36] libsepol: validate policy properties Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 34/36] libsepol: validate categories Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 35/36] libsepol: validate fsuse types Christian Göttsche
2021-12-09 16:49     ` [PATCH v3 36/36] libsepol: validate class default targets Christian Göttsche
2021-12-15 17:41     ` [PATCH v3 00/36] libsepol: add fuzzer for reading binary policies James Carter
2021-12-17 13:59       ` James Carter

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.