1717// KASAN overrides it at runtime — common on syzkaller / CTF / debug kernels.
1818//
1919// On a positive disable signal, pin all three bases to the paging-level default
20- // (L4 = VA 48, L5 = VA 57), chosen by SF_VIRT_ADDR_BITS (proc_cpuinfo's "bits
21- // virtual", which tracks the active level — leak-free, so the pin fires without
22- // any direct-map leak). CONFIG_RANDOMIZE_BASE is independent of the memory
23- // randomisation, so kernel TEXT stays randomised; this pins only the
20+ // (L4 = VA 48, L5 = VA 57). The level comes from SF_VIRT_ADDR_BITS
21+ // (proc_cpuinfo's "bits virtual", which tracks the active level — leak-free, so
22+ // the pin can fire without any direct-map leak), falling back to a resolved
23+ // Q_VA_BITS (e.g. x86_64_la57_from_directmap, from a directmap leak's top bits)
24+ // when cpuinfo is unavailable. CONFIG_RANDOMIZE_BASE is independent of the
25+ // memory randomisation, so kernel TEXT stays randomised; this pins only the
2426// direct-map side.
2527//
2628// Soundness:
2729// * Fires only on a positive disable signal AND a resolved VA width.
2830// * The pinned values are exact, non-config-tunable kernel constants for the
29- // resolved level, so no window-containment read is needed — and an
30- // out-of-window C_EQUALS is dropped by the engine's meet as a conflict
31- // anyway (so no est[Q] read, no self-edge).
31+ // resolved level — no window-containment read is needed, and an
32+ // out-of-window C_EQUALS is dropped by the engine's meet as a conflict.
33+ // * The only estimate read is est[Q_VA_BITS] (the level): cross-quantity, not
34+ // a self-edge on the bases written here, and acyclic — Q_VA_BITS derives
35+ // from the directmap observation, never from est[Q_PAGE_OFFSET].
3236// * A higher-confidence real direct-map leak still wins via the resolver's
3337// conflict handling.
3438// ---
4145int rule_directmap_kaslr_disabled_pin (const struct evidence_set * ev ,
4246 const struct estimate * est ,
4347 struct constraint * out , int out_max ) {
44- (void )est ;
4548#if defined(__x86_64__ ) || defined(__amd64__ )
4649 uint32_t sig_id = 0 , va_id = 0 ;
4750 enum kasld_confidence sig_conf = CONF_UNKNOWN , va_conf = CONF_UNKNOWN ;
@@ -63,7 +66,18 @@ int rule_directmap_kaslr_disabled_pin(const struct evidence_set *ev,
6366 va_conf = o -> conf ;
6467 }
6568 }
66- if (sig_id == 0 || va_bits == 0 )
69+ if (sig_id == 0 )
70+ return 0 ;
71+
72+ /* Paging level (L4 = VA 48 / L5 = VA 57): prefer the leak-free cpuinfo scalar
73+ * (SF_VIRT_ADDR_BITS); fall back to a resolved Q_VA_BITS — e.g.
74+ * x86_64_la57_from_directmap pins it from a directmap leak's top bits when
75+ * cpuinfo is unavailable. Reading est[Q_VA_BITS] is cross-quantity (not a
76+ * self-edge on the bases this rule writes) and acyclic — Q_VA_BITS derives
77+ * from the directmap observation, never from est[Q_PAGE_OFFSET]. */
78+ if (va_bits == 0 )
79+ estimate_finset_value (& quantities [Q_VA_BITS ], & est [Q_VA_BITS ], & va_bits );
80+ if (va_bits == 0 )
6781 return 0 ;
6882
6983 /* Active paging level: 48-bit VA -> 4-level (L4), 57-bit -> 5-level (L5). */
@@ -84,7 +98,11 @@ int rule_directmap_kaslr_disabled_pin(const struct evidence_set *ev,
8498 {Q_VMEMMAP_BASE , l5 ? VMEMMAP_BASE_L5 : VMEMMAP_BASE_L4 },
8599 };
86100
87- enum kasld_confidence conf = (sig_conf < va_conf ) ? sig_conf : va_conf ;
101+ /* When the level came from the cpuinfo scalar, corroborate with its
102+ * confidence + id; when it came from the already-resolved estimate there is
103+ * no observation to cite, so the pin rests on the disable signal alone. */
104+ enum kasld_confidence conf =
105+ va_id ? ((sig_conf < va_conf ) ? sig_conf : va_conf ) : sig_conf ;
88106 int n = 0 ;
89107 for (int k = 0 ; k < 3 && n < out_max ; k ++ ) {
90108 struct constraint * c = & out [n ++ ];
@@ -95,12 +113,13 @@ int rule_directmap_kaslr_disabled_pin(const struct evidence_set *ev,
95113 c -> conf = conf ;
96114 c -> derived_from [0 ] = sig_id ;
97115 c -> derived_from [1 ] = va_id ;
98- c -> lineage_count = 2 ;
116+ c -> lineage_count = va_id ? 2 : 1 ;
99117 snprintf (c -> origin , ORIGIN_LEN , "directmap_kaslr_disabled_pin" );
100118 }
101119 return n ;
102120#else
103121 (void )ev ;
122+ (void )est ;
104123 (void )out ;
105124 (void )out_max ;
106125 return 0 ;
0 commit comments