DF-0592 / leak_sample.txt
# 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).