SELinuxProject / selinux

This is the upstream repository for the Security Enhanced Linux (SELinux) userland libraries and tools. The software provided by this project complements the SELinux features integrated into the Linux kernel and is used by Linux distributions. All bugs and patches should be submitted to selinux@vger.kernel.org
Other
1.35k stars 360 forks source link

checkpolicy/oss-fuzz: add libfuzz based fuzzer #313

Closed cgzones closed 8 months ago

cgzones commented 3 years ago

Introduce a libfuzz1 based fuzzer testing the parsing and policy generation code used within checkpolicy(8) and checkmodule(8), similar to the fuzzer for secilc(8). The fuzzer will work on generated source policy input and try to parse, link, expand, optimize, sort and output it.

Build the fuzzer in the oss-fuzz script.

/cc @fishilico @evverx

I am not familiar how the actual integration into oss-fuzz works and if it needs any update.

evverx commented 3 years ago

@cgzones thanks a lot for working on this! I'll take a look tomorrow.

I'm not sure why CIFuzz didn't report a memory leak it found though:

2021-09-30T16:25:19.7597616Z =================================================================
2021-09-30T16:25:19.7598142Z ==21==ERROR: LeakSanitizer: detected memory leaks
2021-09-30T16:25:19.7598585Z 
2021-09-30T16:25:19.7599025Z Direct leak of 48 byte(s) in 1 object(s) allocated from:
2021-09-30T16:25:19.7600239Z     #0 0x52515d in __interceptor_malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:129:3
2021-09-30T16:25:19.7601187Z     #1 0x57d50f in define_cexpr /src/selinux/checkpolicy/policy_define.c:3751:14
2021-09-30T16:25:19.7601985Z     #2 0x587f2c in yyparse /src/selinux/checkpolicy/policy_parse.y:573:44
2021-09-30T16:25:19.7603088Z     #3 0x55e2c7 in read_source_policy /src/selinux/checkpolicy/fuzz/checkpolicy-fuzzer.c:81:6
2021-09-30T16:25:19.7604424Z     #4 0x55e2c7 in LLVMFuzzerTestOneInput /src/selinux/checkpolicy/fuzz/checkpolicy-fuzzer.c:122:6
2021-09-30T16:25:19.7605595Z     #5 0x456b73 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) cxa_noexception.cpp
2021-09-30T16:25:19.7607020Z     #6 0x45636a in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) cxa_noexception.cpp
2021-09-30T16:25:19.7608077Z     #7 0x457bdb in fuzzer::Fuzzer::MutateAndTestOne() cxa_noexception.cpp
2021-09-30T16:25:19.7609160Z     #8 0x458695 in fuzzer::Fuzzer::Loop(std::__Fuzzer::vector<fuzzer::SizedFile, std::__Fuzzer::allocator<fuzzer::SizedFile> >&) cxa_noexception.cpp
2021-09-30T16:25:19.7610309Z     #9 0x447e30 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) cxa_noexception.cpp
2021-09-30T16:25:19.7611517Z     #10 0x470ec2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
2021-09-30T16:25:19.7612606Z     #11 0x7fbdab3c10b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
2021-09-30T16:25:19.7613120Z 
2021-09-30T16:25:19.7613809Z DEDUP_TOKEN: __interceptor_malloc--define_cexpr--yyparse
2021-09-30T16:25:19.7614561Z Direct leak of 34 byte(s) in 4 object(s) allocated from:
2021-09-30T16:25:19.7615604Z     #0 0x52515d in __interceptor_malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:129:3
2021-09-30T16:25:19.7616496Z     #1 0x56d648 in insert_id /src/selinux/checkpolicy/policy_define.c:126:18
2021-09-30T16:25:19.7617280Z     #2 0x5886a1 in yyparse /src/selinux/checkpolicy/policy_parse.y:877:31
2021-09-30T16:25:19.7618366Z     #3 0x55e2c7 in read_source_policy /src/selinux/checkpolicy/fuzz/checkpolicy-fuzzer.c:81:6
2021-09-30T16:25:19.7619677Z     #4 0x55e2c7 in LLVMFuzzerTestOneInput /src/selinux/checkpolicy/fuzz/checkpolicy-fuzzer.c:122:6
2021-09-30T16:25:19.7620847Z     #5 0x456b73 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) cxa_noexception.cpp
2021-09-30T16:25:19.7622062Z     #6 0x45636a in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) cxa_noexception.cpp
2021-09-30T16:25:19.7623087Z     #7 0x457bdb in fuzzer::Fuzzer::MutateAndTestOne() cxa_noexception.cpp
2021-09-30T16:25:19.7624174Z     #8 0x458695 in fuzzer::Fuzzer::Loop(std::__Fuzzer::vector<fuzzer::SizedFile, std::__Fuzzer::allocator<fuzzer::SizedFile> >&) cxa_noexception.cpp
2021-09-30T16:25:19.7625322Z     #9 0x447e30 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) cxa_noexception.cpp
2021-09-30T16:25:19.7626493Z     #10 0x470ec2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
2021-09-30T16:25:19.7627575Z     #11 0x7fbdab3c10b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
2021-09-30T16:25:19.7628094Z 
2021-09-30T16:25:19.7628762Z DEDUP_TOKEN: __interceptor_malloc--insert_id--yyparse
2021-09-30T16:25:19.7629490Z Indirect leak of 40 byte(s) in 1 object(s) allocated from:
2021-09-30T16:25:19.7630722Z     #0 0x52515d in __interceptor_malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:129:3
2021-09-30T16:25:19.7631673Z     #1 0x59177e in constraint_expr_init /src/selinux/libsepol/src/constraint.c:32:26
2021-09-30T16:25:19.7632510Z     #2 0x57d52b in define_cexpr /src/selinux/checkpolicy/policy_define.c:3752:6
2021-09-30T16:25:19.7633311Z     #3 0x587f2c in yyparse /src/selinux/checkpolicy/policy_parse.y:573:44
2021-09-30T16:25:19.7634420Z     #4 0x55e2c7 in read_source_policy /src/selinux/checkpolicy/fuzz/checkpolicy-fuzzer.c:81:6
2021-09-30T16:25:19.7635742Z     #5 0x55e2c7 in LLVMFuzzerTestOneInput /src/selinux/checkpolicy/fuzz/checkpolicy-fuzzer.c:122:6
2021-09-30T16:25:19.7636870Z     #6 0x456b73 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) cxa_noexception.cpp
2021-09-30T16:25:19.7652310Z     #7 0x45636a in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) cxa_noexception.cpp
2021-09-30T16:25:19.7653305Z     #8 0x457bdb in fuzzer::Fuzzer::MutateAndTestOne() cxa_noexception.cpp
2021-09-30T16:25:19.7654316Z     #9 0x458695 in fuzzer::Fuzzer::Loop(std::__Fuzzer::vector<fuzzer::SizedFile, std::__Fuzzer::allocator<fuzzer::SizedFile> >&) cxa_noexception.cpp
2021-09-30T16:25:19.7655682Z     #10 0x447e30 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) cxa_noexception.cpp
2021-09-30T16:25:19.7657015Z     #11 0x470ec2 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
2021-09-30T16:25:19.7658012Z     #12 0x7fbdab3c10b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
2021-09-30T16:25:19.7658456Z 
2021-09-30T16:25:19.7659178Z DEDUP_TOKEN: __interceptor_malloc--constraint_expr_init--define_cexpr
2021-09-30T16:25:19.7659981Z SUMMARY: AddressSanitizer: 122 byte(s) leaked in 6 allocation(s).
2021-09-30T16:25:19.7661659Z MS: 5 CrossOver-CMP-ChangeASCIIInt-ChangeByte-ChangeByte- DE: "netd"-; base unit: 1a5f664451a502a9bfd88b7bb74d570e3eabf548

It says

2021-09-30T16:25:20.0134894Z 2021-09-30 16:25:20,012 - root - INFO - Reproduce command returned: 0. Not reproducible on /github/workspace/build-out/checkpolicy-fuzzer.
2021-09-30T16:25:20.0144377Z 2021-09-30 16:25:20,013 - root - INFO - Crash is not reproducible.
cgzones commented 3 years ago

I took a look at the build script and apart from min_pol.conf not being put into the seed corpus it looks good to me on the whole.

The fuzzer has the hardcoded setting to always expect a MLS policy (similar to checkpolicy -M), as the type of policy needs to be known a priory with the current parser implementation. I initially wrote also the non-MLS policy for testing, but it is intentionally not used currently (but I included it for reference and potential future use).

cgzones commented 3 years ago

I'm not sure why CIFuzz didn't report a memory leak it found though:

Does oss-fuzz use some custom allocator, that fails unreproducible (to test error branches), cause there are probably a couple dozens memory leaks in error branches in the parser?

evverx commented 3 years ago

Does oss-fuzz use some custom allocator, that fails unreproducible (to test error branches), cause there are probably a couple dozens memory leaks in error branches in the parser?

No, it doesn't. I think that particular memory leak isn't reproducible because it can't be triggered by a single file (according to CIFuzz at least), which probably means that some kind of state accumulates between runs.

evverx commented 3 years ago

@cgzones could you temporarily apply the following patch:

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

so that CIFuzz could upload the file triggering that memory leak to hopefully make it easier to reproduce it locally.

evverx commented 3 years ago

could you temporarily apply the following patch

Done in https://github.com/SELinuxProject/selinux/pull/314

cgzones commented 3 years ago

..., which probably means that some kind of state accumulates between runs.

True, the crash was an empty input, and after I added cleanup to some global variables it seems to be fixed.

evverx commented 3 years ago

FWIW I'm not sure why unreproducible crashes aren't reported by default by CIFuzz but I think I'd keep it on. OSS-Fuzz would just keep opening and closing "flaky" issues making it hard to to figure out what that was.

evverx commented 3 years ago

CIFuzz failed due to a bug in libClusterFuzz. It should be fixed in https://github.com/google/clusterfuzz/pull/2471