CVE-2021-36980: Use After Free (CWE-416)
value: Open vSwitch (aka openvswitch) 2.11.0 through 2.15.0 has a use-after-free in decode_NXAST_RAW_ENCAP (called from ofpact_decode and ofpacts_decode) during the decoding of a RAW_ENCAP action.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 5.5
Published: 2021-07-20T07:15Z
Repository: https://github.com/openvswitch/ovs
Language: C
Files changed: lib/ofp-actions.c, tests/automake.mk, tests/fuzz-regression-list.at
Commit date: 2021-02-17 12:06:45 +0100
Commit message: ofp-actions: Fix use-after-free while decoding RAW_ENCAP.

While decoding RAW_ENCAP action, decode_ed_prop() might re-allocate
ofpbuf if there is no enough space left.  However, function
'decode_NXAST_RAW_ENCAP' continues to use old pointer to 'encap'
structure leading to write-after-free and incorrect decoding.

  ==3549105==ERROR: AddressSanitizer: heap-use-after-free on address
  0x60600000011a at pc 0x0000005f6cc6 bp 0x7ffc3a2d4410 sp 0x7ffc3a2d4408
  WRITE of size 2 at 0x60600000011a thread T0
    #0 0x5f6cc5 in decode_NXAST_RAW_ENCAP lib/ofp-actions.c:4461:20
    #1 0x5f0551 in ofpact_decode ./lib/ofp-actions.inc2:4777:16
    #2 0x5ed17c in ofpacts_decode lib/ofp-actions.c:7752:21
    #3 0x5eba9a in ofpacts_pull_openflow_actions__ lib/ofp-actions.c:7791:13
    #4 0x5eb9fc in ofpacts_pull_openflow_actions lib/ofp-actions.c:7835:12
    #5 0x64bb8b in ofputil_decode_packet_out lib/ofp-packet.c:1113:17
    #6 0x65b6f4 in ofp_print_packet_out lib/ofp-print.c:148:13
    #7 0x659e3f in ofp_to_string__ lib/ofp-print.c:1029:16
    #8 0x659b24 in ofp_to_string lib/ofp-print.c:1244:21
    #9 0x65a28c in ofp_print lib/ofp-print.c:1288:28
    #10 0x540d11 in ofctl_ofp_parse utilities/ovs-ofctl.c:2814:9
    #11 0x564228 in ovs_cmdl_run_command__ lib/command-line.c:247:17
    #12 0x56408a in ovs_cmdl_run_command lib/command-line.c:278:5
    #13 0x5391ae in main utilities/ovs-ofctl.c:179:9
    #14 0x7f6911ce9081 in __libc_start_main (/lib64/libc.so.6+0x27081)
    #15 0x461fed in _start (utilities/ovs-ofctl+0x461fed)

Fix that by getting a new pointer before using.

Credit to OSS-Fuzz.

Fuzzer regression test will fail only with AddressSanitizer enabled.

Reported-at: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=27851
Fixes: f839892a206a ("OF support and translation of generic encap and decap")
Acked-by: William Tu <u9012063@gmail.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>

Diff: diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index e2e829772..0342a228b 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -4429,10 +4429,11 @@ decode_NXAST_RAW_ENCAP(const struct nx_action_encap *nae,
                        enum ofp_version ofp_version OVS_UNUSED,
                        struct ofpbuf *out)
 {
     struct ofpact_encap *encap;
     const struct ofp_ed_prop_header *ofp_prop;
+    const size_t encap_ofs = out->size;
     size_t props_len;
     uint16_t n_props = 0;
     int err;
 
     encap = ofpact_put_ENCAP(out);
@@ -4456,10 +4457,11 @@ decode_NXAST_RAW_ENCAP(const struct nx_action_encap *nae,
         if (err) {
             return err;
         }
         n_props++;
     }
+    encap = ofpbuf_at_assert(out, encap_ofs, sizeof *encap);
     encap->n_props = n_props;
     out->header = &encap->ofpact;
     ofpact_finish_ENCAP(out, &encap);
 
     return 0;
diff --git a/tests/automake.mk b/tests/automake.mk
index 677b99a6b..fc80e027d 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -132,11 +132,12 @@ FUZZ_REGRESSION_TESTS = \
 	tests/fuzz-regression/ofp_print_fuzzer-5677588436484096 \
 	tests/fuzz-regression/ofp_print_fuzzer-5706562554298368 \
 	tests/fuzz-regression/ofp_print_fuzzer-5722747668791296 \
 	tests/fuzz-regression/ofp_print_fuzzer-6285128790704128 \
 	tests/fuzz-regression/ofp_print_fuzzer-6470117922701312 \
-	tests/fuzz-regression/ofp_print_fuzzer-6502620041576448
+	tests/fuzz-regression/ofp_print_fuzzer-6502620041576448 \
+	tests/fuzz-regression/ofp_print_fuzzer-6540965472632832
 $(srcdir)/tests/fuzz-regression-list.at: tests/automake.mk
 	$(AM_V_GEN)for name in $(FUZZ_REGRESSION_TESTS); do \
             basename=`echo $$name | sed 's,^.*/,,'`; \
 	    echo "TEST_FUZZ_REGRESSION([$$basename])"; \
 	done > $@.tmp && mv $@.tmp $@
diff --git a/tests/fuzz-regression-list.at b/tests/fuzz-regression-list.at
index e3173fb88..2347c690e 100644
--- a/tests/fuzz-regression-list.at
+++ b/tests/fuzz-regression-list.at
@@ -19,5 +19,6 @@ TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5677588436484096])
 TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5706562554298368])
 TEST_FUZZ_REGRESSION([ofp_print_fuzzer-5722747668791296])
 TEST_FUZZ_REGRESSION([ofp_print_fuzzer-6285128790704128])
 TEST_FUZZ_REGRESSION([ofp_print_fuzzer-6470117922701312])
 TEST_FUZZ_REGRESSION([ofp_print_fuzzer-6502620041576448])
+TEST_FUZZ_REGRESSION([ofp_print_fuzzer-6540965472632832])
diff --git a/tests/fuzz-regression/ofp_print_fuzzer-6540965472632832 b/tests/fuzz-regression/ofp_print_fuzzer-6540965472632832
new file mode 100644
index 000000000..e69de29bb

Vulnerable code: 	tests/fuzz-regression/ofp_print_fuzzer-6502620041576448
Fixed code:     const size_t encap_ofs = out->size;
    encap = ofpbuf_at_assert(out, encap_ofs, sizeof *encap);
	tests/fuzz-regression/ofp_print_fuzzer-6502620041576448 \
	tests/fuzz-regression/ofp_print_fuzzer-6540965472632832
TEST_FUZZ_REGRESSION([ofp_print_fuzzer-6540965472632832])