DF-3723 / chksum_bounds_demo.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | /* DF-3723 โ bound-math demonstration for ixgbe_checksum_ptr_x550()/ixgbe_calc_checksum_X550() * * The guest has no X550 NIC, so the HAL cannot be exercised on hardware. This * harness replicates the EXACT four guard checks from * sys/dev/netif/ix/ixgbe_x550.c (lines 3358, 3368, 3376, 3430 as of this * audit) and the checksum accumulation loop (3379-3395), with the NVM reads * replaced by a seeded in-memory image. It shows that a caller-provided * `buffer/buffer_size` pair that passes every check in the shipped code still * makes the loop read past the end of that buffer. * * Build: cc -O2 -o chksum_bounds_demo chksum_bounds_demo.c * Run: ./chksum_bounds_demo */ #include <stdio.h> #include <string.h> #include <stdint.h> typedef uint16_t u16; typedef uint32_t u32; #define WORD_SIZE 4096 /* hw->eeprom.word_size for a typical X550 NVM */ #define IXGBE_ERR_PARAM (-12) /* ---- replica of the shipped checks + loop (buffer != NULL path) ---- */ static int shipped_checksum_ptr(u16 ptr, u16 size, u16 *csum, u16 *buffer, u32 buffer_size, u32 *oob_first_index, long buffer_len_words) { u16 length, start, i; u16 *local_buffer; long oob_reads = 0; /* ixgbe_x550.c:3358 */ if (buffer_size < ptr) return IXGBE_ERR_PARAM; /* ixgbe_x550.c:3360 */ local_buffer = &buffer[ptr]; if (size) { start = 0; length = size; } else { start = 1; /* ixgbe_x550.c:3368 โ length word read at buffer[ptr] */ length = local_buffer[0]; /* ixgbe_x550.c:3371 โ skip if invalid (all pass here) */ if (length == 0xFFFF || length == 0 || (ptr + length) >= WORD_SIZE) return 0; } /* ixgbe_x550.c:3376 โ THE DEFECT: bounds checked against * start+length while reads occur at buffer[ptr + i] */ if ((u32)start + (u32)length > buffer_size) return IXGBE_ERR_PARAM; /* ixgbe_x550.c:3379-3395 */ for (i = start; length; i++, length--) { long idx = (long)ptr + i; /* actual word touched */ if (idx >= buffer_len_words) { if (oob_reads == 0) *oob_first_index = (u32)idx; oob_reads++; } *csum += local_buffer[i]; } return oob_reads ? -100 : 0; /* -100 == "OOB observed" */ } /* ---- replica of the fixed checks ---- */ static int fixed_checksum_ptr(u16 ptr, u16 size, u16 *csum, u16 *buffer, u32 buffer_size, u32 *oob_first_index, long buffer_len_words) { u16 length, start, i; u16 *local_buffer; long oob_reads = 0; /* fix #1: reject ptr == buffer_size (local_buffer[0] read at :3368) */ if (buffer_size <= ptr) return IXGBE_ERR_PARAM; local_buffer = &buffer[ptr]; if (size) { start = 0; length = size; } else { start = 1; length = local_buffer[0]; if (length == 0xFFFF || length == 0 || (ptr + length) >= WORD_SIZE) return 0; } /* fix #2: account for the ptr base */ if ((u32)ptr + (u32)start + (u32)length > buffer_size) return IXGBE_ERR_PARAM; for (i = start; length; i++, length--) { long idx = (long)ptr + i; if (idx >= buffer_len_words) { if (oob_reads == 0) *oob_first_index = (u32)idx; oob_reads++; } *csum += local_buffer[i]; } return oob_reads ? -100 : 0; } int main(void) { /* caller buffer of 100 words (200 bytes) โ e.g. a caller that read a * 100-word NVM window and calls calc_checksum(hw, buf, 100) */ long buflen = 100; static u16 image[WORD_SIZE]; u16 csum; u32 oob = 0; int rc; /* seeded "NVM": pointer word at 0x02 (IXGBE_PCIE_ANALOG_PTR_X550) * points at 90 (inside the 100-word buffer), and the word at offset * 90 declares a section length of 50 words */ image[2] = 90; image[90] = 50; printf("buffer_size=%ld words, ptr=%u, length-at-ptr=%u\n", buflen, image[2], image[90]); csum = 0; rc = shipped_checksum_ptr(image[2], 0, &csum, image /* buffer==image is NOT the scenario; use dedicated buf below */, 0, &oob, 0); (void)rc; /* the real scenario: caller buffer is its own 100-word array */ { static u16 callerbuf[100]; callerbuf[0] = image[90]; /* word at ptr=0? no โ ptr=90: */ memset(callerbuf, 0xAB, sizeof(callerbuf)); /* place the section-length word at callerbuf[90] like the * NVM image would: the guard reads local_buffer[0] i.e. * buffer[ptr] */ callerbuf[90] = 50; oob = 0; csum = 0; rc = shipped_checksum_ptr(90, 0, &csum, callerbuf, 100, &oob, 100); if (rc == -100) printf("SHIPPED: OOB READ โ first out-of-bounds word " "index = %u (buffer holds 0..99); reads run to " "buffer[90+50]=buffer[140]\n", oob); else if (rc == IXGBE_ERR_PARAM) printf("SHIPPED: rejected (ERR_PARAM) โ no OOB\n"); else printf("SHIPPED: accepted, in-bounds only\n"); oob = 0; csum = 0; rc = fixed_checksum_ptr(90, 0, &csum, callerbuf, 100, &oob, 100); if (rc == -100) printf("FIXED: OOB READ (fix failed!) idx=%u\n", oob); else if (rc == IXGBE_ERR_PARAM) printf("FIXED: rejected (ERR_PARAM) โ OOB prevented\n"); else printf("FIXED: accepted, in-bounds only\n"); } /* off-by-one demo: buffer_size == ptr (ixgbe_x550.c:3358/3368) */ { static u16 small[90]; memset(small, 0xCD, sizeof(small)); oob = 0; csum = 0; rc = shipped_checksum_ptr(90, 0, &csum, small, 90, &oob, 90); printf("OFF-BY-ONE (buffer_size == ptr == 90): SHIPPED %s" " (reads length at buffer[90] โ one word past end)\n", rc == IXGBE_ERR_PARAM ? "rejects" : "ACCEPTS and reads buffer[90]"); rc = fixed_checksum_ptr(90, 0, &csum, small, 90, &oob, 90); printf("OFF-BY-ONE: FIXED %s\n", rc == IXGBE_ERR_PARAM ? "rejects" : "accepts"); } return 0; } |