https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=218911

            Bug ID: 218911
           Summary: [uma] Memory corruption with certain item sizes
           Product: Base System
           Version: 11.0-STABLE
          Hardware: Any
                OS: Any
            Status: New
          Keywords: patch
          Severity: Affects Some People
          Priority: ---
         Component: kern
          Assignee: freebsd-bugs@FreeBSD.org
          Reporter: f...@fabiankeil.de

Created attachment 182122
  --> https://bugs.freebsd.org/bugzilla/attachment.cgi?id=182122&action=edit
sys/vm: Set UMA_ZONE_OFFPAGE if the offset would be too large to fit into
keg->uk_pgoff

When creating an uma zone without specifying the UMA_ZONE_OFFPAGE
flag uma may use the slab itself for its book-keeping purposes if
it contains enough unused space.

keg->uk_pgoff is an uint16_t. It can overflow in which case
valid writes to correctly allocated items corrupt uma's
book-keeping data.

With INVARIANTS enabled, this results in a panic in uma_dbg_free(),
without INVARIANTS the effect is less obvious.

Here's a backtrace from a panic with INVARIANTS enabled
using a test zone with item size 129024 which is one of
the sizes that trigger the problem:

(kgdb) where
#0  __curthread () at ./machine/pcpu.h:222
#1  doadump (textdump=1) at /usr/src/sys/kern/kern_shutdown.c:298
#2  0xffffffff80555465 in kern_reboot (howto=260) at
/usr/src/sys/kern/kern_shutdown.c:366
#3  0xffffffff80555a40 in vpanic (fmt=<optimized out>, ap=0xfffffe0113d69980)
at /usr/src/sys/kern/kern_shutdown.c:759
#4  0xffffffff80555876 in kassert_panic (fmt=0xffffffff808c521f "Assertion %s
failed at %s:%d") at /usr/src/sys/kern/kern_shutdown.c:649
#5  0xffffffff807dd333 in keg_fetch_slab (keg=0xfffff80005b52480,
zone=0xfffff80005b53700, flags=2) at /usr/src/sys/vm/uma_core.c:2343
#6  0xffffffff807dc9ae in zone_fetch_slab (zone=0xfffff80005b53700,
keg=0xfffff80005b52480, flags=2) at /usr/src/sys/vm/uma_core.c:2368
#7  0xffffffff807dca40 in zone_import (zone=0xfffff80005b53700,
bucket=0xfffff800d7eae9f8, max=1, flags=2) at /usr/src/sys/vm/uma_core.c:2494
#8  0xffffffff807d9341 in zone_alloc_bucket (zone=0xfffff80005b53700,
udata=0x0, flags=2) at /usr/src/sys/vm/uma_core.c:2524
#9  uma_zalloc_arg (zone=0xfffff80005b53700, udata=0x0, flags=<optimized out>)
at /usr/src/sys/vm/uma_core.c:2250
#10 0xffffffff8182329f in uma_zalloc (flags=2, zone=<optimized out>) at
/usr/src/sys/vm/uma.h:336
#11 g_raid_init (mp=0xffffffff81856838 <g_raid_class>) at
/usr/src/sys/modules/geom/geom_raid/../../../geom/raid/g_raid.c:2496
#12 0xffffffff804d1125 in g_load_class (arg=<optimized out>, flag=<optimized
out>) at /usr/src/sys/geom/geom_subr.c:124
#13 0xffffffff804cd7c7 in one_event () at /usr/src/sys/geom/geom_event.c:264
#14 g_run_events () at /usr/src/sys/geom/geom_event.c:286
#15 0xffffffff80517e64 in fork_exit (callout=0xffffffff804cfa60
<g_event_procbody>, arg=0x0, frame=0xfffffe0113d69c00) at
/usr/src/sys/kern/kern_fork.c:1040
#16 <signal handler called>
[...]
(kgdb) f 5
#5  0xffffffff807dd333 in keg_fetch_slab (keg=0xfffff80005b52480,
zone=0xfffff80005b53700, flags=2) at /usr/src/sys/vm/uma_core.c:2343
2343                            MPASS(slab->us_keg == keg);
(kgdb) p slab->us_keg
$1 = (uma_keg_t) 0xdeadc0dedeadc0de
(kgdb) f 9
#9  uma_zalloc_arg (zone=0xfffff80005b53700, udata=0x0, flags=<optimized out>)
at /usr/src/sys/vm/uma_core.c:2250
2250            bucket = zone_alloc_bucket(zone, udata, flags);
(kgdb) p zone
$2 = (uma_zone_t) 0xfffff80005b53700
(kgdb) p *zone
$3 = {uz_lock = {lock_object = {lo_name = 0xffffffff8184f85a "uma_test",
lo_flags = 21168128, lo_data = 0, lo_witness = 0xfffffe0000a0cf80}, mtx_lock =
4}, uz_lockptr = 0xfffff80005b52480,
  uz_name = 0xffffffff8184f85a "uma_test", uz_link = {le_next = 0x0, le_prev =
0xfffff80005b52510}, uz_buckets = {lh_first = 0x0}, uz_kegs = {lh_first =
0xfffff80005b537b0}, uz_klink = {kl_link = {le_next = 0x0,
      le_prev = 0xfffff80005b537a8}, kl_keg = 0xfffff80005b52480}, uz_slab =
0xffffffff807dc940 <zone_fetch_slab>, uz_ctor = 0xffffffff807dd730
<trash_ctor>, uz_dtor = 0xffffffff807dd780 <trash_dtor>, uz_init = 0x0,
  uz_fini = 0x0, uz_import = 0xffffffff807dc9f0 <zone_import>, uz_release =
0xffffffff807dcc90 <zone_release>, uz_arg = 0xfffff80005b53700, uz_flags = 0,
uz_size = 129024, uz_allocs = 0, uz_fails = 0, uz_frees = 0,
  uz_sleeps = 0, uz_count = 1, uz_count_min = 1, uz_warning = 0x0, uz_ratecheck
= {tv_sec = 0, tv_usec = 0}, uz_maxaction = {ta_link = {stqe_next = 0x0},
ta_pending = 0, ta_priority = 0, ta_func = 0x0, ta_context = 0x0},
0x0},
  uz_cpu = {{uc_freebucket = 0x0, uc_allocbucket = 0x0, uc_allocs = 0, uc_frees
= 0}}}
(kgdb) f 11
#11 g_raid_init (mp=0xffffffff81856838 <g_raid_class>) at
/usr/src/sys/modules/geom/geom_raid/../../../geom/raid/g_raid.c:2496
2496                    p = uma_zalloc(uma_test_zone, M_WAITOK);
(kgdb) p i
$4 = 0
(kgdb) l -
2491
2492            uma_test_zone = uma_zcreate("uma_test",
uma_test_zone_item_size, NULL, NULL,
2493                NULL, NULL, 0, 0);
2494
2495            for (i = 0; i <= uma_test_zone_item_size; i++) {
2496                    p = uma_zalloc(uma_test_zone, M_WAITOK);
2497                    memset(p, 0x10, i);
2498                    G_RAID_DEBUG(1, "Freeing item after filling %d bytes.",
i);
2499                    uma_zfree(uma_test_zone, p);
2500            }
(kgdb) f 9
#9  uma_zalloc_arg (zone=0xfffff80005b53700, udata=0x0, flags=<optimized out>)
at /usr/src/sys/vm/uma_core.c:2250
2250            bucket = zone_alloc_bucket(zone, udata, flags);
(kgdb) p zone->uz_kegs->lh_first->kl_keg
$6 = (uma_keg_t) 0xfffff80005b52480
(kgdb) p *zone->uz_kegs->lh_first->kl_keg
$7 = {uk_lock = {lock_object = {lo_name = 0xffffffff8184f85a "uma_test",
lo_flags = 21168128, lo_data = 0, lo_witness = 0xfffffe0000a0cf80}, mtx_lock =
18446735277663592448}, uk_hash = {uh_slab_hash = 0x0, uh_hashsize = 0,
    uh_hashmask = 0}, uk_zones = {lh_first = 0xfffff80005b53700}, uk_part_slab
= {lh_first = 0x0}, uk_free_slab = {lh_first = 0x0}, uk_full_slab = {lh_first =
0x0}, uk_align = 0, uk_pages = 32, uk_free = 1, uk_reserve = 0,
  uk_size = 129024, uk_rsize = 129024, uk_maxpages = 0, uk_init =
0xffffffff807dd7b0 <trash_init>, uk_fini = 0xffffffff807dd7e0 <trash_fini>,
uk_allocf = 0xffffffff807db690 <page_alloc>,
  uk_freef = 0xffffffff807db770 <page_free>, uk_offset = 0, uk_kva = 0,
uk_slabzone = 0x0, uk_pgoff = 65424, uk_ppera = 32, uk_ipers = 1, uk_flags = 0,
uk_name = 0xffffffff8184f85a "uma_test", uk_link = {
    le_next = 0xfffff80005b52300, le_prev = 0xffffffff80f45d00 <uma_kegs>}}
(kgdb) p sizeof(struct uma_slab)
$8 = 112
(kgdb) p zone->uz_kegs->lh_first->kl_keg->uk_ppera
$9 = 32
(kgdb) p 4096 * zone->uz_kegs->lh_first->kl_keg->uk_ppera
$10 = 131072
(kgdb) p 4096 * zone->uz_kegs->lh_first->kl_keg->uk_ppera - sizeof(struct
uma_slab)
$11 = 130960
(kgdb) p 4096 * zone->uz_kegs->lh_first->kl_keg->uk_ppera - sizeof(struct
uma_slab) - 65536
$12 = 65424
(kgdb) p zone->uz_kegs->lh_first->kl_keg->uk_size
$13 = 129024
(kgdb) p 4096 * zone->uz_kegs->lh_first->kl_keg->uk_ppera - 
zone->uz_kegs->lh_first->kl_keg->uk_rsize
$14 = 2048

The attached patch lets uma set the UMA_ZONE_OFFPAGE if keg->uk_pgoff
would otherwise be used but is too small to hold the correct offset.

Changing keg->uk_pgoff's type should work as well but I haven't tested it.

Obtained from: ElectroBSD

-- 
You are receiving this mail because:
You are the assignee for the bug.
_______________________________________________
freebsd-bugs@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "freebsd-bugs-unsubscr...@freebsd.org"

Reply via email to