DF-4582 / rs690_div0_body.h
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 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 | /* * DF-4582 โ verbatim extraction of the vulnerable rs690 fixed-point math. * * Contents (each copied byte-identical from the DragonFly tree): * - fixed20_12 dfixed_* macros ........ sys/dev/drm/include/drm/drm_fixed.h:30-70 * - do_div ............................. sys/dev/drm/include/linux/math64.h:36-42 * - struct rs690_watermark ............. sys/dev/drm/radeon/rs690.c:257-268 * - rs690_crtc_bandwidth_compute() ..... sys/dev/drm/radeon/rs690.c:270-457 * * The only changes are the surrounding stub structs (rdev/crtc fields the * function reads), collected here so the file compiles standalone in * userland and as a DragonFly KLD. The FUNCTION BODY is untouched. * * df4582_scenario selects the attacker-controlled input: * 0 : mode->clock = 1048576 kHz (2^20) โ the DRM_IOCTL_MODE_SETCRTC wrap * (div-by-zero at rs690.c:329, pclk = dfixed_div(1000, 0)) * 1 : VBIOS igp_sideport_mclk = 0 with sideport enabled * (div-by-zero at rs690.c:372, DF-4583) * 2 : VBIOS default_sclk = 0 => rdev->pm.sclk == 0 * (div-by-zero at rs690.c:388, DF-4583) */ #ifndef DF4582_BODY_H #define DF4582_BODY_H #include <stdint.h> #ifndef KERNEL #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #define KPRINTF(...) do { fprintf(stderr, __VA_ARGS__); fflush(stderr); } while (0) #else #include <sys/systm.h> #include <sys/types.h> #define KPRINTF kprintf #endif typedef uint32_t u32; typedef uint64_t u64; typedef uint8_t u8; typedef uint16_t u16; #define DRM_ERROR(fmt, ...) KPRINTF("DF4582-DRM_ERROR: " fmt, ##__VA_ARGS__) /* ---- verbatim: sys/dev/drm/include/linux/math64.h:36-42, upper/lower_32_bits * from linux/kernel.h:132-133 (must precede dfixed_div, exactly as * math64.h is included before drm_fixed.h in the kernel build) ---- */ #define do_div(n, base) ({ \ uint32_t __base = (base); \ uint32_t __rem; \ __rem = ((uint64_t)(n)) % __base; \ (n) = ((uint64_t)(n)) / __base; \ __rem; \ }) #define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) #define lower_32_bits(n) ((u32)(n)) /* ---- end verbatim helpers ---- */ /* ---- verbatim: sys/dev/drm/include/drm/drm_fixed.h:30-70 ---- */ typedef union dfixed { u32 full; } fixed20_12; #define dfixed_const(A) (u32)(((A) << 12))/* + ((B + 0.000122)*4096)) */ #define dfixed_const_half(A) (u32)(((A) << 12) + 2048) #define dfixed_const_666(A) (u32)(((A) << 12) + 2731) #define dfixed_const_8(A) (u32)(((A) << 12) + 3277) #define dfixed_mul(A, B) ((u64)((u64)(A).full * (B).full + 2048) >> 12) #define dfixed_init(A) { .full = dfixed_const((A)) } #define dfixed_init_half(A) { .full = dfixed_const_half((A)) } #define dfixed_trunc(A) ((A).full >> 12) #define dfixed_frac(A) ((A).full & ((1 << 12) - 1)) static inline u32 dfixed_floor(fixed20_12 A) { u32 non_frac = dfixed_trunc(A); return dfixed_const(non_frac); } static inline u32 dfixed_ceil(fixed20_12 A) { u32 non_frac = dfixed_trunc(A); if (A.full > dfixed_const(non_frac)) return dfixed_const(non_frac + 1); else return dfixed_const(non_frac); } /* * The KERNEL build marks dfixed_div noinline so gcc cannot UB-fuse the * inner (by-constant) and outer divisions โ the SHIPPED radeon.ko code * (objdump 0x5ac39-0x5ac61) keeps them separate: truncating shl $0xc, * magic-mul for the inner, then a plain runtime `div %rcx` by the * possibly-zero result. noinline only prevents an optimization the * real build also happened not to perform; the arithmetic is unchanged. */ #ifdef KERNEL __attribute__((noinline)) static u32 dfixed_div(fixed20_12 A, fixed20_12 B) #else static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B) #endif { u64 tmp = ((u64)A.full << 13); do_div(tmp, B.full); tmp += 1; tmp /= 2; return lower_32_bits(tmp); } /* ---- end drm_fixed.h verbatim ---- */ /* * Stub structs: same field names/types the real function reaches through. * (struct radeon_device / radeon_crtc carry hundreds of fields; only the * ones touched by rs690_crtc_bandwidth_compute are reproduced here.) */ enum radeon_family { CHIP_RS690 = 1, CHIP_RS780, CHIP_RS880 }; enum { PM_METHOD_PROFILE, PM_METHOD_DPM }; enum rmx_type_t { RMX_OFF, RMX_FULL, RMX_CENTER, RMX_ASPECT }; struct df4582_mode { int clock; /* in kHz โ exactly like drm_modes.h:278 */ u32 hdisplay, hsync_start, hsync_end, htotal; u32 vdisplay, vsync_start, vsync_end, vtotal; u32 crtc_hdisplay, crtc_hsync_start, crtc_hsync_end, crtc_htotal; u32 crtc_vdisplay, crtc_vsync_start, crtc_vsync_end, crtc_vtotal; }; struct df4582_pm { u32 current_sclk; /* 10 kHz units */ fixed20_12 sclk; fixed20_12 igp_sideport_mclk; fixed20_12 igp_system_mclk; fixed20_12 sideport_bandwidth; fixed20_12 k8_bandwidth; fixed20_12 ht_bandwidth; int pm_method; int dpm_enabled; }; struct df4582_mc { int igp_sideport_enabled; }; struct df4582_rdev { enum radeon_family family; struct df4582_pm pm; struct df4582_mc mc; }; struct df4582_crtc { struct { int enabled; struct df4582_mode mode; } base; fixed20_12 vsc, hsc; enum rmx_type_t rmx_type; }; #define radeon_device df4582_rdev #define radeon_crtc df4582_crtc #define drm_display_mode df4582_mode /* ---- verbatim: sys/dev/drm/radeon/rs690.c:257-268 ---- */ struct rs690_watermark { u32 lb_request_fifo_depth; fixed20_12 num_line_pair; fixed20_12 estimated_width; fixed20_12 worst_case_latency; fixed20_12 consumption_rate; fixed20_12 active_time; fixed20_12 dbpp; fixed20_12 priority_mark_max; fixed20_12 priority_mark; fixed20_12 sclk; }; /* guard under test โ compiled in only with -DDF4582_FIXED (mirrors fix.diff) */ #ifdef DF4582_FIXED #define DF4582_GUARD_ZERO(x, tag) do { if ((x) == 0) { \ KPRINTF("DF4582-FIX: zero divisor avoided at %s โ bailing with safe watermark\n", tag); \ wm->lb_request_fifo_depth = 4; \ return; \ } } while (0) #define DF4582_GUARD_ZERO_KEEP(x, tag) do { if ((x) == 0) { \ KPRINTF("DF4582-FIX: zero mclk avoided at %s โ using UMA latency\n", tag); \ } } while (0) #endif /* ---- verbatim: sys/dev/drm/radeon/rs690.c:270-457 (body UNCHANGED) ---- */ static void rs690_crtc_bandwidth_compute(struct radeon_device *rdev, struct radeon_crtc *crtc, struct rs690_watermark *wm, int low) { struct drm_display_mode *mode = &crtc->base.mode; fixed20_12 a, b, c; fixed20_12 pclk, request_fifo_depth, tolerable_latency, estimated_width; fixed20_12 consumption_time, line_time, chunk_time, read_delay_latency; fixed20_12 sclk, core_bandwidth, max_bandwidth; u32 selected_sclk; bzero(wm, sizeof(*wm)); /* avoid gcc warning */ if (!crtc->base.enabled) { /* FIXME: wouldn't it better to set priority mark to maximum */ wm->lb_request_fifo_depth = 4; return; } if (((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880)) && (rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) selected_sclk = 0; /* radeon_dpm_get_sclk(rdev, low); not reached */ else selected_sclk = rdev->pm.current_sclk; /* sclk in Mhz */ a.full = dfixed_const(100); sclk.full = dfixed_const(selected_sclk); sclk.full = dfixed_div(sclk, a); /* core_bandwidth = sclk(Mhz) * 16 */ a.full = dfixed_const(16); core_bandwidth.full = dfixed_div(rdev->pm.sclk, a); if (crtc->vsc.full > dfixed_const(2)) wm->num_line_pair.full = dfixed_const(2); else wm->num_line_pair.full = dfixed_const(1); b.full = dfixed_const(mode->crtc_hdisplay); c.full = dfixed_const(256); a.full = dfixed_div(b, c); request_fifo_depth.full = dfixed_mul(a, wm->num_line_pair); request_fifo_depth.full = dfixed_ceil(request_fifo_depth); if (a.full < dfixed_const(4)) { wm->lb_request_fifo_depth = 4; } else { wm->lb_request_fifo_depth = dfixed_trunc(request_fifo_depth); } /* Determine consumption rate * pclk = pixel clock period(ns) = 1000 / (mode.clock / 1000) * vtaps = number of vertical taps, * vsc = vertical scaling ratio, defined as source/destination * hsc = horizontal scaling ration, defined as source/destination */ a.full = dfixed_const(mode->clock); b.full = dfixed_const(1000); a.full = dfixed_div(a, b); #ifdef DF4582_FIXED /* DF-4582 fix: (u32)(clock << 12) wraps to 0 for clock โก 0 (mod 2^20) */ DF4582_GUARD_ZERO(a.full, "pclk"); #endif pclk.full = dfixed_div(b, a); if (crtc->rmx_type != RMX_OFF) { b.full = dfixed_const(2); if (crtc->vsc.full > b.full) b.full = crtc->vsc.full; b.full = dfixed_mul(b, crtc->hsc); c.full = dfixed_const(2); b.full = dfixed_div(b, c); consumption_time.full = dfixed_div(pclk, b); } else { consumption_time.full = pclk.full; } a.full = dfixed_const(1); wm->consumption_rate.full = dfixed_div(a, consumption_time); /* Determine line time * LineTime = total time for one line of displayhtotal * LineTime = total number of horizontal pixels * pclk = pixel clock period(ns) */ a.full = dfixed_const(crtc->base.mode.crtc_htotal); line_time.full = dfixed_mul(a, pclk); /* Determine active time * ActiveTime = time of active region of display within one line, * hactive = total number of horizontal active pixels * htotal = total number of horizontal pixels */ a.full = dfixed_const(crtc->base.mode.crtc_htotal); b.full = dfixed_const(crtc->base.mode.crtc_hdisplay); wm->active_time.full = dfixed_mul(line_time, b); wm->active_time.full = dfixed_div(wm->active_time, a); /* Maximun bandwidth is the minimun bandwidth of all component */ max_bandwidth = core_bandwidth; if (rdev->mc.igp_sideport_enabled) { if (max_bandwidth.full > rdev->pm.sideport_bandwidth.full && rdev->pm.sideport_bandwidth.full) max_bandwidth = rdev->pm.sideport_bandwidth; #ifdef DF4582_FIXED /* DF-4583 fix: zero sideport mclk from VBIOS must not divide */ DF4582_GUARD_ZERO_KEEP(rdev->pm.igp_sideport_mclk.full, "rdl"); #endif read_delay_latency.full = dfixed_const(370 * 800); a.full = dfixed_const(1000); b.full = dfixed_div(rdev->pm.igp_sideport_mclk, a); #ifdef DF4582_FIXED DF4582_GUARD_ZERO(b.full, "rdl-mclk"); #endif read_delay_latency.full = dfixed_div(read_delay_latency, b); read_delay_latency.full = dfixed_mul(read_delay_latency, a); } else { if (max_bandwidth.full > rdev->pm.k8_bandwidth.full && rdev->pm.k8_bandwidth.full) max_bandwidth = rdev->pm.k8_bandwidth; if (max_bandwidth.full > rdev->pm.ht_bandwidth.full && rdev->pm.ht_bandwidth.full) max_bandwidth = rdev->pm.ht_bandwidth; read_delay_latency.full = dfixed_const(5000); } /* sclk = system clocks(ns) = 1000 / max_bandwidth / 16 */ a.full = dfixed_const(16); sclk.full = dfixed_mul(max_bandwidth, a); a.full = dfixed_const(1000); #ifdef DF4582_FIXED /* DF-4583 fix: all-zero VBIOS clocks must not divide */ DF4582_GUARD_ZERO(sclk.full, "1000/sclk"); #endif sclk.full = dfixed_div(a, sclk); /* Determine chunk time * ChunkTime = the time it takes the DCP to send one chunk of data * to the LB which consists of pipeline delay and inter chunk gap * sclk = system clock(ns) */ a.full = dfixed_const(256 * 13); chunk_time.full = dfixed_mul(sclk, a); a.full = dfixed_const(10); chunk_time.full = dfixed_div(chunk_time, a); /* Determine the worst case latency * NumLinePair = Number of line pairs to request(1=2 lines, 2=4 lines) * WorstCaseLatency = worst case time from urgent to when the MC starts * to return data * READ_DELAY_IDLE_MAX = constant of 1us * ChunkTime = time it takes the DCP to send one chunk of data to the LB * which consists of pipeline delay and inter chunk gap */ if (dfixed_trunc(wm->num_line_pair) > 1) { a.full = dfixed_const(3); wm->worst_case_latency.full = dfixed_mul(a, chunk_time); wm->worst_case_latency.full += read_delay_latency.full; } else { a.full = dfixed_const(2); wm->worst_case_latency.full = dfixed_mul(a, chunk_time); wm->worst_case_latency.full += read_delay_latency.full; } /* Determine the tolerable latency * TolerableLatency = Any given request has only 1 line time * for the data to be returned * LBRequestFifoDepth = Number of chunk requests the LB can * put into the request FIFO for a display * LineTime = total time for one line of display * ChunkTime = the time it takes the DCP to send one chunk * of data to the LB which consists of * pipeline delay and inter chunk gap */ if ((2+wm->lb_request_fifo_depth) >= dfixed_trunc(request_fifo_depth)) { tolerable_latency.full = line_time.full; } else { tolerable_latency.full = dfixed_const(wm->lb_request_fifo_depth - 2); tolerable_latency.full = request_fifo_depth.full - tolerable_latency.full; tolerable_latency.full = dfixed_mul(tolerable_latency, chunk_time); tolerable_latency.full = line_time.full - tolerable_latency.full; } /* We assume worst case 32bits (4 bytes) */ wm->dbpp.full = dfixed_const(4 * 8); /* Determine the maximum priority mark * width = viewport width in pixels */ a.full = dfixed_const(16); wm->priority_mark_max.full = dfixed_const(crtc->base.mode.crtc_hdisplay); wm->priority_mark_max.full = dfixed_div(wm->priority_mark_max, a); wm->priority_mark_max.full = dfixed_ceil(wm->priority_mark_max); /* Determine estimated width */ estimated_width.full = tolerable_latency.full - wm->worst_case_latency.full; estimated_width.full = dfixed_div(estimated_width, consumption_time); if (dfixed_trunc(estimated_width) > crtc->base.mode.crtc_hdisplay) { wm->priority_mark.full = dfixed_const(10); } else { a.full = dfixed_const(16); wm->priority_mark.full = dfixed_div(estimated_width, a); wm->priority_mark.full = dfixed_ceil(wm->priority_mark); wm->priority_mark.full = wm->priority_mark_max.full - wm->priority_mark.full; } KPRINTF("DF4582: compute completed without division (scenario reached end)\n"); } /* ---- end verbatim function ---- */ #undef radeon_device #undef radeon_crtc #undef drm_display_mode /* * Attacker-shaped inputs. */ static void df4582_setup(struct df4582_rdev *rdev, struct df4582_crtc *crtc, int scenario) { memset(rdev, 0, sizeof(*rdev)); memset(crtc, 0, sizeof(*crtc)); rdev->family = CHIP_RS690; rdev->pm.pm_method = PM_METHOD_PROFILE; /* realistic IGP numbers (all from a healthy BIOS) */ rdev->pm.current_sclk = 40000; /* 400 MHz (10 kHz) */ rdev->pm.sclk.full = dfixed_const(400); /* radeon_update_.. */ rdev->pm.igp_system_mclk.full = dfixed_const(667); rdev->pm.igp_sideport_mclk.full = dfixed_const(667); rdev->pm.k8_bandwidth.full = dfixed_const(400 * 4); rdev->pm.ht_bandwidth.full = dfixed_const(8000 / 5); rdev->pm.sideport_bandwidth.full = dfixed_const(900); rdev->mc.igp_sideport_enabled = 0; crtc->base.enabled = 1; crtc->rmx_type = RMX_OFF; /* a plain 640x480@60-ish mode ... */ crtc->base.mode.clock = 25175; /* kHz */ crtc->base.mode.hdisplay = 640; crtc->base.mode.htotal = 800; crtc->base.mode.crtc_hdisplay = 640; crtc->base.mode.crtc_htotal = 800; switch (scenario) { case 0: /* DF-4582: pixel clock chosen by the DRM user */ crtc->base.mode.clock = 1048576; /* 2^20 kHz โ wraps to 0 */ break; case 1: /* DF-4583: VBIOS says sideport present, boot clock 0 */ rdev->mc.igp_sideport_enabled = 1; rdev->pm.igp_sideport_mclk.full = 0; break; case 2: /* DF-4583: VBIOS ulDefaultEngineClock == 0 */ rdev->pm.sclk.full = 0; rdev->pm.k8_bandwidth.full = dfixed_const(100); rdev->pm.ht_bandwidth.full = dfixed_const(100); break; } } #endif /* DF4582_BODY_H */ |