Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kasan: catch invalid free before SLUB reinitializes the object

Currently, when KASAN is combined with init-on-free behavior, the
initialization happens before KASAN's "invalid free" checks.

More importantly, a subsequent commit will want to RCU-delay the actual
SLUB freeing of an object, and we'd like KASAN to still validate
synchronously that freeing the object is permitted. (Otherwise this
change will make the existing testcase kmem_cache_invalid_free fail.)

So add a new KASAN hook that allows KASAN to pre-validate a
kmem_cache_free() operation before SLUB actually starts modifying the
object or its metadata.

Inside KASAN, this:

- moves checks from poison_slab_object() into check_slab_allocation()
- moves kasan_arch_is_ready() up into callers of poison_slab_object()
- removes "ip" argument of poison_slab_object() and __kasan_slab_free()
(since those functions no longer do any reporting)

Acked-by: Vlastimil Babka <vbabka@suse.cz> #slub
Reviewed-by: Andrey Konovalov <andreyknvl@gmail.com>
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Vlastimil Babka <vbabka@suse.cz>

authored by

Jann Horn and committed by
Vlastimil Babka
b3c34245 4e1c44b3

+95 -29
+51 -3
include/linux/kasan.h
··· 175 175 return (void *)object; 176 176 } 177 177 178 - bool __kasan_slab_free(struct kmem_cache *s, void *object, 179 - unsigned long ip, bool init); 178 + bool __kasan_slab_pre_free(struct kmem_cache *s, void *object, 179 + unsigned long ip); 180 + /** 181 + * kasan_slab_pre_free - Check whether freeing a slab object is safe. 182 + * @object: Object to be freed. 183 + * 184 + * This function checks whether freeing the given object is safe. It may 185 + * check for double-free and invalid-free bugs and report them. 186 + * 187 + * This function is intended only for use by the slab allocator. 188 + * 189 + * @Return true if freeing the object is unsafe; false otherwise. 190 + */ 191 + static __always_inline bool kasan_slab_pre_free(struct kmem_cache *s, 192 + void *object) 193 + { 194 + if (kasan_enabled()) 195 + return __kasan_slab_pre_free(s, object, _RET_IP_); 196 + return false; 197 + } 198 + 199 + bool __kasan_slab_free(struct kmem_cache *s, void *object, bool init); 200 + /** 201 + * kasan_slab_free - Poison, initialize, and quarantine a slab object. 202 + * @object: Object to be freed. 203 + * @init: Whether to initialize the object. 204 + * 205 + * This function informs that a slab object has been freed and is not 206 + * supposed to be accessed anymore, except for objects in 207 + * SLAB_TYPESAFE_BY_RCU caches. 208 + * 209 + * For KASAN modes that have integrated memory initialization 210 + * (kasan_has_integrated_init() == true), this function also initializes 211 + * the object's memory. For other modes, the @init argument is ignored. 212 + * 213 + * This function might also take ownership of the object to quarantine it. 214 + * When this happens, KASAN will defer freeing the object to a later 215 + * stage and handle it internally until then. The return value indicates 216 + * whether KASAN took ownership of the object. 217 + * 218 + * This function is intended only for use by the slab allocator. 219 + * 220 + * @Return true if KASAN took ownership of the object; false otherwise. 221 + */ 180 222 static __always_inline bool kasan_slab_free(struct kmem_cache *s, 181 223 void *object, bool init) 182 224 { 183 225 if (kasan_enabled()) 184 - return __kasan_slab_free(s, object, _RET_IP_, init); 226 + return __kasan_slab_free(s, object, init); 185 227 return false; 186 228 } 187 229 ··· 413 371 { 414 372 return (void *)object; 415 373 } 374 + 375 + static inline bool kasan_slab_pre_free(struct kmem_cache *s, void *object) 376 + { 377 + return false; 378 + } 379 + 416 380 static inline bool kasan_slab_free(struct kmem_cache *s, void *object, bool init) 417 381 { 418 382 return false;
+37 -26
mm/kasan/common.c
··· 208 208 return (void *)object; 209 209 } 210 210 211 - static inline bool poison_slab_object(struct kmem_cache *cache, void *object, 212 - unsigned long ip, bool init) 211 + /* Returns true when freeing the object is not safe. */ 212 + static bool check_slab_allocation(struct kmem_cache *cache, void *object, 213 + unsigned long ip) 213 214 { 214 - void *tagged_object; 215 + void *tagged_object = object; 215 216 216 - if (!kasan_arch_is_ready()) 217 - return false; 218 - 219 - tagged_object = object; 220 217 object = kasan_reset_tag(object); 221 218 222 219 if (unlikely(nearest_obj(cache, virt_to_slab(object), object) != object)) { ··· 221 224 return true; 222 225 } 223 226 224 - /* RCU slabs could be legally used after free within the RCU period. */ 225 - if (unlikely(cache->flags & SLAB_TYPESAFE_BY_RCU)) 226 - return false; 227 - 228 227 if (!kasan_byte_accessible(tagged_object)) { 229 228 kasan_report_invalid_free(tagged_object, ip, KASAN_REPORT_DOUBLE_FREE); 230 229 return true; 231 230 } 231 + 232 + return false; 233 + } 234 + 235 + static inline void poison_slab_object(struct kmem_cache *cache, void *object, 236 + bool init) 237 + { 238 + void *tagged_object = object; 239 + 240 + object = kasan_reset_tag(object); 241 + 242 + /* RCU slabs could be legally used after free within the RCU period. */ 243 + if (unlikely(cache->flags & SLAB_TYPESAFE_BY_RCU)) 244 + return; 232 245 233 246 kasan_poison(object, round_up(cache->object_size, KASAN_GRANULE_SIZE), 234 247 KASAN_SLAB_FREE, init); 235 248 236 249 if (kasan_stack_collection_enabled()) 237 250 kasan_save_free_info(cache, tagged_object); 238 - 239 - return false; 240 251 } 241 252 242 - bool __kasan_slab_free(struct kmem_cache *cache, void *object, 243 - unsigned long ip, bool init) 253 + bool __kasan_slab_pre_free(struct kmem_cache *cache, void *object, 254 + unsigned long ip) 244 255 { 245 - if (is_kfence_address(object)) 256 + if (!kasan_arch_is_ready() || is_kfence_address(object)) 257 + return false; 258 + return check_slab_allocation(cache, object, ip); 259 + } 260 + 261 + bool __kasan_slab_free(struct kmem_cache *cache, void *object, bool init) 262 + { 263 + if (!kasan_arch_is_ready() || is_kfence_address(object)) 246 264 return false; 247 265 248 - /* 249 - * If the object is buggy, do not let slab put the object onto the 250 - * freelist. The object will thus never be allocated again and its 251 - * metadata will never get released. 252 - */ 253 - if (poison_slab_object(cache, object, ip, init)) 254 - return true; 266 + poison_slab_object(cache, object, init); 255 267 256 268 /* 257 269 * If the object is put into quarantine, do not let slab put the object ··· 510 504 return true; 511 505 } 512 506 513 - if (is_kfence_address(ptr)) 514 - return false; 507 + if (is_kfence_address(ptr) || !kasan_arch_is_ready()) 508 + return true; 515 509 516 510 slab = folio_slab(folio); 517 - return !poison_slab_object(slab->slab_cache, ptr, ip, false); 511 + 512 + if (check_slab_allocation(slab->slab_cache, ptr, ip)) 513 + return false; 514 + 515 + poison_slab_object(slab->slab_cache, ptr, false); 516 + return true; 518 517 } 519 518 520 519 void __kasan_mempool_unpoison_object(void *ptr, size_t size, unsigned long ip)
+7
mm/slub.c
··· 2227 2227 return false; 2228 2228 2229 2229 /* 2230 + * Give KASAN a chance to notice an invalid free operation before we 2231 + * modify the object. 2232 + */ 2233 + if (kasan_slab_pre_free(s, x)) 2234 + return false; 2235 + 2236 + /* 2230 2237 * As memory initialization might be integrated into KASAN, 2231 2238 * kasan_slab_free and initialization memset's must be 2232 2239 * kept together to avoid discrepancies in behavior.