Merge branch 'slab/for-6.1/kmalloc_size_roundup' into slab/for-next
authorVlastimil Babka <vbabka@suse.cz>
Thu, 29 Sep 2022 09:30:55 +0000 (11:30 +0200)
committerVlastimil Babka <vbabka@suse.cz>
Thu, 29 Sep 2022 09:30:55 +0000 (11:30 +0200)
The first two patches from a series by Kees Cook [1] that introduce
kmalloc_size_roundup(). This will allow merging of per-subsystem patches using
the new function and ultimately stop (ab)using ksize() in a way that causes
ongoing trouble for debugging functionality and static checkers.

[1] https://lore.kernel.org/all/20220923202822.2667581-1-keescook@chromium.org/

--
Resolved a conflict of modifying mm/slab.c __ksize() comment with a commit that
unifies __ksize() implementation into mm/slab_common.c

1  2 
include/linux/slab.h
mm/slab_common.c
mm/slob.c

Simple merge
@@@ -895,144 -922,6 +915,155 @@@ void __init create_kmalloc_caches(slab_
        /* Kmalloc array is now usable */
        slab_state = UP;
  }
- /* Uninstrumented ksize. Only called by KASAN. */
 +
 +void free_large_kmalloc(struct folio *folio, void *object)
 +{
 +      unsigned int order = folio_order(folio);
 +
 +      if (WARN_ON_ONCE(order == 0))
 +              pr_warn_once("object pointer: 0x%p\n", object);
 +
 +      kmemleak_free(object);
 +      kasan_kfree_large(object);
 +
 +      mod_lruvec_page_state(folio_page(folio, 0), NR_SLAB_UNRECLAIMABLE_B,
 +                            -(PAGE_SIZE << order));
 +      __free_pages(folio_page(folio, 0), order);
 +}
 +
 +static void *__kmalloc_large_node(size_t size, gfp_t flags, int node);
 +static __always_inline
 +void *__do_kmalloc_node(size_t size, gfp_t flags, int node, unsigned long caller)
 +{
 +      struct kmem_cache *s;
 +      void *ret;
 +
 +      if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) {
 +              ret = __kmalloc_large_node(size, flags, node);
 +              trace_kmalloc(_RET_IP_, ret, size,
 +                            PAGE_SIZE << get_order(size), flags, node);
 +              return ret;
 +      }
 +
 +      s = kmalloc_slab(size, flags);
 +
 +      if (unlikely(ZERO_OR_NULL_PTR(s)))
 +              return s;
 +
 +      ret = __kmem_cache_alloc_node(s, flags, node, size, caller);
 +      ret = kasan_kmalloc(s, ret, size, flags);
 +      trace_kmalloc(_RET_IP_, ret, size, s->size, flags, node);
 +      return ret;
 +}
 +
 +void *__kmalloc_node(size_t size, gfp_t flags, int node)
 +{
 +      return __do_kmalloc_node(size, flags, node, _RET_IP_);
 +}
 +EXPORT_SYMBOL(__kmalloc_node);
 +
 +void *__kmalloc(size_t size, gfp_t flags)
 +{
 +      return __do_kmalloc_node(size, flags, NUMA_NO_NODE, _RET_IP_);
 +}
 +EXPORT_SYMBOL(__kmalloc);
 +
 +void *__kmalloc_node_track_caller(size_t size, gfp_t flags,
 +                                int node, unsigned long caller)
 +{
 +      return __do_kmalloc_node(size, flags, node, caller);
 +}
 +EXPORT_SYMBOL(__kmalloc_node_track_caller);
 +
 +/**
 + * kfree - free previously allocated memory
 + * @object: pointer returned by kmalloc.
 + *
 + * If @object is NULL, no operation is performed.
 + *
 + * Don't free memory not originally allocated by kmalloc()
 + * or you will run into trouble.
 + */
 +void kfree(const void *object)
 +{
 +      struct folio *folio;
 +      struct slab *slab;
 +      struct kmem_cache *s;
 +
 +      trace_kfree(_RET_IP_, object);
 +
 +      if (unlikely(ZERO_OR_NULL_PTR(object)))
 +              return;
 +
 +      folio = virt_to_folio(object);
 +      if (unlikely(!folio_test_slab(folio))) {
 +              free_large_kmalloc(folio, (void *)object);
 +              return;
 +      }
 +
 +      slab = folio_slab(folio);
 +      s = slab->slab_cache;
 +      __kmem_cache_free(s, (void *)object, _RET_IP_);
 +}
 +EXPORT_SYMBOL(kfree);
 +
++/**
++ * __ksize -- Report full size of underlying allocation
++ * @objp: pointer to the object
++ *
++ * This should only be used internally to query the true size of allocations.
++ * It is not meant to be a way to discover the usable size of an allocation
++ * after the fact. Instead, use kmalloc_size_roundup(). Using memory beyond
++ * the originally requested allocation size may trigger KASAN, UBSAN_BOUNDS,
++ * and/or FORTIFY_SOURCE.
++ *
++ * Return: size of the actual memory used by @objp in bytes
++ */
 +size_t __ksize(const void *object)
 +{
 +      struct folio *folio;
 +
 +      if (unlikely(object == ZERO_SIZE_PTR))
 +              return 0;
 +
 +      folio = virt_to_folio(object);
 +
 +      if (unlikely(!folio_test_slab(folio))) {
 +              if (WARN_ON(folio_size(folio) <= KMALLOC_MAX_CACHE_SIZE))
 +                      return 0;
 +              if (WARN_ON(object != folio_address(folio)))
 +                      return 0;
 +              return folio_size(folio);
 +      }
 +
 +      return slab_ksize(folio_slab(folio)->slab_cache);
 +}
 +
 +#ifdef CONFIG_TRACING
 +void *kmalloc_trace(struct kmem_cache *s, gfp_t gfpflags, size_t size)
 +{
 +      void *ret = __kmem_cache_alloc_node(s, gfpflags, NUMA_NO_NODE,
 +                                          size, _RET_IP_);
 +
 +      trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags, NUMA_NO_NODE);
 +
 +      ret = kasan_kmalloc(s, ret, size, gfpflags);
 +      return ret;
 +}
 +EXPORT_SYMBOL(kmalloc_trace);
 +
 +void *kmalloc_node_trace(struct kmem_cache *s, gfp_t gfpflags,
 +                       int node, size_t size)
 +{
 +      void *ret = __kmem_cache_alloc_node(s, gfpflags, node, size, _RET_IP_);
 +
 +      trace_kmalloc(_RET_IP_, ret, size, s->size, gfpflags, node);
 +
 +      ret = kasan_kmalloc(s, ret, size, gfpflags);
 +      return ret;
 +}
 +EXPORT_SYMBOL(kmalloc_node_trace);
 +#endif /* !CONFIG_TRACING */
  #endif /* !CONFIG_SLOB */
  
  gfp_t kmalloc_fix_flags(gfp_t flags)
diff --cc mm/slob.c
Simple merge