Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time

Disclosure

Date Detail
05/21/2018 reported to Samsung
07/27/2018 vulnerabilities confirmed and assigned SVE-2018-11784, rated Low
07/29/2018 assigned CVE-2018-14745
---------- Awaiting publication of CVE description

Affected Product

Samsung Galaxy S6 (SM-G920F), Firmware Version G920FXXU5EQH7

Vulnerabilities

A potential buffer overflow in driver/net/wireless/bcmdhd4538/dhd_msgbuf.c:4212.

4194 /* Assumes only one index is updated ata time */
4195 static void *BCMFASTPATH
4196 prot_get_ring_space(msgbuf_ring_t *ring, uint16 nitems, uint16 * alloced)
4197 {
4198    void *ret_ptr = NULL;
4199    uint16 ring_avail_cnt;
4200
4201    ASSERT(nitems <= RING_MAX_ITEM(ring));
4202
4203    ring_avail_cnt = CHECK_WRITE_SPACE(RING_READ_PTR(ring), RING_WRITE_PTR(ring),
4204       RING_MAX_ITEM(ring));
4205
4206    if (ring_avail_cnt == 0) {
4207       return NULL;
4208    }
4209    *alloced = MIN(nitems, ring_avail_cnt);
4210
4211    /* Return next available space */
4212    ret_ptr = (char*)HOST_RING_BASE(ring) + (RING_WRITE_PTR(ring) * RING_LEN_ITEMS(ring));
4213
4214    /* Update write pointer */
4215    if ((RING_WRITE_PTR(ring) + *alloced) == RING_MAX_ITEM(ring))
4216       RING_WRITE_PTR(ring) = 0;
4217    else if ((RING_WRITE_PTR(ring) + *alloced) < RING_MAX_ITEM(ring))
4218       RING_WRITE_PTR(ring) += *alloced;
4219    else {
4220       /* Should never hit this */
4221       ASSERT(0);
4222       return NULL;
4223    }
4224
4225    return ret_ptr;
4226 }

Analysis

In line driver/net/wireless/bcmdhd4538/dhd_msgbuf.c:4203 the number of available write items in the ringbuffer ring_avail_cnt is calculated based on a value controlled by the attacker on the WiFi SOC.

The value of ring_avail_count is calculated by a macro defined in driver/net/wireless/bcmdhd4538/include/bcmpcie.h:226.

199 #define NTXPACTIVE(r, w, d)     (((r) <= (w)) ? ((w)-(r)) : ((d)-(r)+(w)))
...
223 #define WRITE_SPACE_AVAIL_CONTINUOUS(r, w, d)      ((w >= r) ? (d - w) : (r - w))
224 #define WRITE_SPACE_AVAIL(r, w, d)  (d - (NTXPACTIVE(r, w, d)) - 1)
225 #define CHECK_WRITE_SPACE(r, w, d)  \
226    MIN(WRITE_SPACE_AVAIL(r, w, d), WRITE_SPACE_AVAIL_CONTINUOUS(r, w, d))

In line driver/net/wireless/bcmdhd4538/dhd_msgbuf.c:3833 the function prot_get_ring_space is invoked, which initializes the ringbuffer read pointer value by reading from memory that can be modified by the attacker on WiFi SoC in line driver/net/wireless/bcmdhd4538/dhd_pcie.c:2369.

3818 static void * BCMFASTPATH
3819 dhd_alloc_ring_space(dhd_pub_t *dhd, msgbuf_ring_t *ring, uint16 nitems, uint16 * alloced)
3820 {  
3821    void * ret_buf;
3822    uint16 r_index = 0;
3823    
3824    /* Alloc space for nitems in the ring */
3825    ret_buf = prot_get_ring_space(ring, nitems, alloced);
3826    
3827    if (ret_buf == NULL) {
3828       /* if alloc failed , invalidate cached read ptr */
3829       if (DMA_INDX_ENAB(dhd->dma_d2h_ring_upd_support)) {
3830          r_index = dhd_get_dmaed_index(dhd, H2D_DMA_READINDX, ring->idx);
3831          ring->ringstate->r_offset = r_index;
3832       } else
3833          dhd_bus_cmn_readshared(dhd->bus, &(RING_READ_PTR(ring)),
3834             RING_READ_PTR, ring->idx);
3835 
3836       /* Try allocating once more */
3837       ret_buf = prot_get_ring_space(ring, nitems, alloced);
3838 
3839       if (ret_buf == NULL) {
3840          DHD_INFO(("RING space not available on ring %s for %d items \n",
3841             ring->name, nitems));
3842          DHD_INFO(("write %d read %d \n\n", RING_WRITE_PTR(ring),
3843             RING_READ_PTR(ring)));
3844          return NULL;
3845       }
3846    }
3847 
3848    /* Return alloced space */
3849    return ret_buf;
3850 }
2354 void  
2355 dhd_bus_cmn_readshared(dhd_bus_t *bus, void* data, uint8 type, uint16 ringid)                                  
2356 {
2357    pciedev_shared_t *sh;
2358    ulong tcm_offset;
2359             
2360    sh = (pciedev_shared_t*)bus->shared_addr;
2361             
2362    switch (type) {
2363       case RING_WRITE_PTR :
2364          tcm_offset = bus->ring_sh[ringid].ring_state_w;
2365          *(uint16*)data = LTOH16(dhdpcie_bus_rtcm16(bus, tcm_offset));
2366          break;
2367       case RING_READ_PTR :
2368          tcm_offset = bus->ring_sh[ringid].ring_state_r;
2369          *(uint16*)data = LTOH16(dhdpcie_bus_rtcm16(bus, tcm_offset));
2370          break;
2371       case TOTAL_LFRAG_PACKET_CNT :
2372          *(uint16*)data = LTOH16(dhdpcie_bus_rtcm16(bus,
2373             (ulong) &sh->total_lfrag_pkt_cnt));
2374          break;
2375       case HTOD_MB_DATA:
2376          *(uint32*)data = LTOH32(dhdpcie_bus_rtcm32(bus, bus->h2d_mb_data_ptr_addr));
2377          break;
2378       case DTOH_MB_DATA:
2379          *(uint32*)data = LTOH32(dhdpcie_bus_rtcm32(bus, bus->d2h_mb_data_ptr_addr));
2380          break;
2381       case MAX_HOST_RXBUFS :
2382          *(uint16*)data = LTOH16(dhdpcie_bus_rtcm16(bus,
2383             (ulong) &sh->max_host_rxbufs));
2384          break;
2385       default :
2386          break;
2387    }
2388 }  

Therefore, In line driver/net/wireless/bcmdhd4538/dhd_msgbuf.c:4203 the attacker controls the value of RING_READ_PTR(ring). By choosing a value much higher that RING_WRITE_PTR(ring) for RING_READ_PTR(ring) the macro CHECK_WRITE_SPACE will evaluate MIN(d - d + r - w - 1, r - w) or simply r - w, which might exceed the actually available ringbuffer space.

Kernel-Trace

The bug was discovered based on the warning generated by the ASSERT statement in driver/net/wireless/bcmdhd4538/dhd_msgbuf.c:4221, which is shown at the end of the kernel trace below.

...
[  103.120949]  [0:    kworker/0:3: 4824] usb: android_work config=          (null),connected=1,sw_connected=0
[  103.120969] I[0:    kworker/0:3: 4824] usb: set_config_number single config num=0
[  103.120980] I[0:    kworker/0:3: 4824] usb: config_buf f->adb
[  103.120994] I[0:    kworker/0:3: 4824] usb: set_interface_count next_interface_id=1
[  103.121066] I[0:    kworker/0:3: 4824] usb: set_config_number single config num=0
[  103.121076] I[0:    kworker/0:3: 4824] usb: config_buf f->adb
[  103.121086] I[0:    kworker/0:3: 4824] usb: set_interface_count next_interface_id=1
[  103.121501]  [0:    kworker/0:3: 4824] usb: android_work sent uevent USB_STATE=CONNECTED
[  103.121997] I[0:      swapper/0:    0] android_usb gadget: high-speed config #1: android
[  103.122020] I[0:      swapper/0:    0] usb: set_config e adb[0]
[  103.122083] I[0:      swapper/0:    0] usb: SET_CON
[  103.122095] I[0:      swapper/0:    0] usb: set_config_number single config num=0
[  103.122177]  [0:    kworker/0:3: 4824] usb: android_work config=ffffffc0014cf508,connected=1,sw_connected=1
[  103.122319]  [0:    kworker/0:3: 4824] usb: android_work sent uevent USB_STATE=CONFIGURED
[  103.151044]  [0:    kworker/0:3: 4824] [Time-to-FULL] 0(secs), 0(mins)
[  103.151315]  [0:    kworker/0:3: 4824] [Time-to-EMPTY] 368634(secs), 6143(mins)
[  103.183440]  [0: thread_hotplug: 1881] frequency info : 600000, prev_cmd 4, exe_cmd 1
[  103.183489]  [0: thread_hotplug: 1881] lcd is on : 0, low power mode = 0, dm_hotplug disable = 0
[  103.183515]  [0: thread_hotplug: 1881] cluster1 cores hotplug out : 0
[  103.186031]  [3:    migration/3:   23] IRQ151 no longer affine to CPU3
[  103.187658]  [0: thread_hotplug: 1881] CPU3: shutdown
[  103.189707]  [0: thread_hotplug: 1881] MobiCore mcd: <- core_num=0x00000000, active_cpu=0x00000002
[  103.189836]  [2:    mc_fastcall: 1585] MobiCore mcd: CoreSwap ok 2 -> 0
[  103.191276]  [2:    migration/2:   18] IRQ150 no longer affine to CPU2
[  103.192863]  [0: thread_hotplug: 1881] CPU2: shutdown
[  103.198361]  [1:    migration/1:   13] IRQ149 no longer affine to CPU1
[  103.199880]  [0: thread_hotplug: 1881] CPU1: shutdown
[  103.210177]  [0:    kworker/0:3: 4824] [FG] 0080h,ffa5h,7f80h,ff01h,1900h,11abh,63edh,5767h,1acdh,d798h,00a6h,0135h,004eh,5da0h,65f4h,1204h,11b6h,ffffh,3380h,5c00h,046ah,0148h,1a9ah,010fh,1439h,d72dh,290fh,d992h,41b8h,0314h,0578h,11b7h,0000h,20b0h,2000h,1327h,1400h,2305h,1a75h,7f6ch,2472h,cea4h,203bh,09d0h,e3e1h,290eh,0400h,0000h,4d07h,45c1h,1603h,02d7h,0000h,11aeh,ebc8h,05e0h,0035h,1a45h,7d54h,f9fbh,1680h,1408h,5484h,e000h,0000h,b00dh,1005h,0000h,d881h,07ddh,521dh,ffdah,5e15h,2271h,1204h,0000h,0adfh,0ad7h,107eh,0000h,0000h,0000h,7f80h,0000h,0000h,0005h,0073h,0a53h,0000h,006bh,090ch,0050h,00efh,0204h,051ah,0000h,03ddh,100fh,0c0eh,0c0dh,0c0ch,0c0bh,0c0ah,0c09h,0c08h,a516h,4350h,0010h,004eh,ffffh,65edh,11b6h,07f3h,2472h,0012h,0012h,0012h,0002h,1204h,0000h,4c30h,0004h,4240h,0000h,4000h,0000h,7d00h,0620h,0000h,0000h,0000h,0000h,22cfh,0000h,0000h,f55eh,3fb6h,5a8bh,ff5fh,d84fh,002fh,2910h,d798h,5da4h,
[  103.210483]  [0:    kworker/0:3: 4824] max77843_fg_read_current: current=-72
[  103.210973]  [0:    kworker/0:3: 4824] max77843_fg_read_avg_current: avg_current=46
[  103.211943]  [0:    kworker/0:3: 4824] max77843_fg_write_temp: temperature to (268, 0x1acd)
[  103.212398]  [0:    kworker/0:3: 4824] max77843_fg_check_qrtable: QRTABLE20_REG(0x1603), QRTABLE30_REG(0x1005)
[  103.216370]  [0:    kworker/0:3: 4824] max77843_fg_read_current: current=-72
[  103.216789]  [0:    kworker/0:3: 4824] max77843_fg_read_vcell: VCELL(4311), data(0xd798)
[  103.216808]  [0:    kworker/0:3: 4824] max77843_fg_read_avg_current: avg_current=46
[  103.217041]  [0:    kworker/0:3: 4824] max77843_get_fuelgauge_soc: soc(999), low_batt_alarm(0)
[  103.217064]  [0:    kworker/0:3: 4824] sec_bat_get_property cable type = 4 sleep_mode = 0
[  103.217262]  [0:    kworker/0:3: 4824] max77843_chg_get_property : set-current(460mA), current now(300mA)
[  103.217459]  [0:    kworker/0:3: 4824] max77843_fg_get_scaled_capacity : CABLE TYPE(4) INPUT CURRENT(300) CHARGINGE MODE(CC Mode)
[  103.217478]  [0:    kworker/0:3: 4824] max77843_fg_get_scaled_capacity: capacity_max (989)
[  103.217692]  [0:    kworker/0:3: 4824] max77843_fg_get_scaled_capacity: scaled capacity (101.0)
[  103.217712]  [0:    kworker/0:3: 4824] max77843_fg_get_atomic_capacity : NOW(100), OLD(100)
[  103.217749]  [0:    kworker/0:3: 4824] sec-battery battery.53: sec_bat_get_battery_info:Vnow(4311mV),Inow(-72mA),Isysnow(-1mA),Imax(300mA),SOC(100%),Tbat(268),Tchg(293),Twpc(0),is_hc_usb(0)
[  103.217774]  [0:    kworker/0:3: 4824] sec-battery battery.53: Connected,Vavg(4303mV),Vocv(4326mV),Tamb(268),Iavg(46mA),Isysavg(-1mA),Iadc(0)
[  103.218153]  [0:    kworker/0:3: 4824] sec-battery battery.53: sec_bat_cisd_check: [CISD] iavg: 46, incur: 300, chgcur: 450,
[  103.218153]  [0:    kworker/0:3: 4824] lcd_off_T: 0, cc_T: 0, passed_T: 81, full_T: 0, chg_end_T: 0, cisd: 0x0
[  103.218189]  [0:    kworker/0:3: 4824] sec-battery battery.53: sec_bat_cisd_check: cisd - clear EFG
[  103.218218]  [0:    kworker/0:3: 4824] sec-battery battery.53: sec_bat_cisd_check: [CISD] iavg: 46, incur: 300, chgcur: 450,
[  103.218218]  [0:    kworker/0:3: 4824] lcd_off_T: 0, cc_T: 0, passed_T: 81, full_T: 0, chg_end_T: 0, recnt: 0, cisd: 0x0
[  103.218646]  [0:    kworker/0:3: 4824] cisd - stt:0, cp:2265/0, cpmm:65535/2265/0, dcpt:0, ovc:0, rct:2147483647
[  103.218670]  [0:    kworker/0:3: 4824] cisd_debug: 0, 0, 65535, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2147483647, 1
[  103.218870]  [0:    kworker/0:3: 4824] calc_ttf: cc_time: 36
[  103.218891]  [0:    kworker/0:3: 4824] calc_ttf: cap: 2588, soc:  999, T:     36, now:  -72, avg:   46, cv soc: 1000, i:   85, val: 250, CC Mode
[  103.218913]  [0:    kworker/0:3: 4824] sec-battery battery.53: sec_bat_calc_time_to_full: T:    60 sec, passed time:    81
[  103.219395]  [0:    kworker/0:3: 4824] max77843_get_charging_health: reg_data(0x3)
[  103.219594]  [0:    kworker/0:3: 4824] sec_bat_get_property cable type = 4 sleep_mode = 0
[  103.219612]  [0:    kworker/0:3: 4824] max77843_get_vbus_state: VBUS is valid. CHGIN < CHGIN_OVLO
[  103.219974]  [0:    kworker/0:3: 4824] max77843_get_charging_health: vbus_state : 0x3, chg_dtls : 0x1
[  103.220006]  [0:    kworker/0:3: 4824] sec-battery battery.53: sec_bat_monitor_work: Status(Charging), mode(Normal), Health(Good), Cable(4,1), level(100%), HV(NONE), Cycle(-1)
[  103.220029]  [0:    kworker/0:3: 4824] power_supply battery: power_supply_changed
[  103.220127]  [0:    kworker/0:3: 4824] max77843_chg_get_property: slow-charging mode
[  103.220156]  [0:    kworker/0:3: 4824] sec_bat_get_property cable type = 4 sleep_mode = 0
[  103.220187]  [0:    kworker/0:3: 4824] sec_bat_get_property cable type = 4 
[  103.223610]  [0:        healthd: 3000] healthd: max77843-charger: Unknown power supply type
[  103.224019]  [0:        healthd: 3000] max77843_get_float_voltage: battery cv voltage 0x1d, chg_float_voltage = 4375mV 
[  105.493519]  [0: thread_hotplug: 1881] frequency info : 1400000, prev_cmd 1, exe_cmd 4
[  105.493545]  [0: thread_hotplug: 1881] lcd is on : 0, low power mode = 1, dm_hotplug disable = 0
[  105.493556]  [0: thread_hotplug: 1881] cluster1 cores hotplug out : 0
[  105.493870]  [1:      swapper/1:    0] CPU1: Booted secondary processor
[  105.495465]  [2:      swapper/2:    0] CPU2: Booted secondary processor
[  105.496625]  [0: thread_hotplug: 1881] MobiCore mcd: <- core_num=0x00000002, active_cpu=0x00000000
[  105.496719]  [0:    mc_fastcall: 1585] MobiCore mcd: CoreSwap ok 0 -> 2
[  105.497165]  [3:      swapper/3:    0] CPU3: Booted secondary processor
[  105.511390] I[0:      swapper/0:    0] "0": file "dhd_msgbuf.c", line 4221
[  105.511603] I[0:      swapper/0:    0] "0": file "dhd_msgbuf.c", line 4221
[  105.511615] I[0:      swapper/0:    0] dhd_prot_rxbufpost:2074: Rxbufpost Msgbuf Not available
[  105.593591]  [0: thread_hotplug: 1881] frequency info : 400000, prev_cmd 4, exe_cmd 1
[  105.593682]  [0: thread_hotplug: 1881] lcd is on : 0, low power mode = 0, dm_hotplug disable = 0
[  105.593720]  [0: thread_hotplug: 1881] cluster1 cores hotplug out : 0
[  105.598033]  [3:    migration/3:   23] IRQ151 no longer affine to CPU3
[  105.600690]  [0: thread_hotplug: 1881] CPU3: shutdown
[  105.604615]  [0: thread_hotplug: 1881] MobiCore mcd: <- core_num=0x00000000, active_cpu=0x00000002
[  105.604822]  [2:    mc_fastcall: 1585] MobiCore mcd: CoreSwap ok 2 -> 0
[  105.609966]  [2:    migration/2:   18] IRQ150 no longer affine to CPU2
[  105.612619]  [0: thread_hotplug: 1881] CPU2: shutdown
[  105.620694]  [1:    migration/1:   13] IRQ149 no longer affine to CPU1
[  105.623578]  [0: thread_hotplug: 1881] CPU1: shutdown
[  106.637930] I[0:      swapper/0:    0] "0": file "dhd_msgbuf.c", line 4221
[  106.638215] I[0:      swapper/0:    0] "0": file "dhd_msgbuf.c", line 4221
[  106.638256] I[0:      swapper/0:    0] dhd_prot_rxbufpost:2074: Rxbufpost Msgbuf Not available
[  107.515009] I[0:      swapper/0:    0] "0": file "dhd_msgbuf.c", line 4221
[  107.515290] I[0:      swapper/0:    0] "0": file "dhd_msgbuf.c", line 4221
[  107.515332] I[0:      swapper/0:    0] dhd_prot_rxbufpost:2074: Rxbufpost Msgbuf Not available
[  108.512249] I[0:      swapper/0:    0] "0": file "dhd_msgbuf.c", line 4221
[  108.512527] I[0:      swapper/0:    0] "0": file "dhd_msgbuf.c", line 4221
[  108.512569] I[0:      swapper/0:    0] dhd_prot_rxbufpost:2074: Rxbufpost Msgbuf Not available
[  109.523213] I[0:      swapper/0:    0] "0": file "dhd_msgbuf.c", line 4221
[  109.523491] I[0:      swapper/0:    0] "0": file "dhd_msgbuf.c", line 4221
[  109.523787] I[0:      swapper/0:    0] dhd_prot_rxbufpost:2074: Rxbufpost Msgbuf Not available
[  110.094278]  [0:  kworker/u16:6: 3406] vfsspi_work_func_debug ldo:3, sleep:0, irq:0, tz:1, type:viper, cnt_irq:0, adm: 0
[  110.516988] I[0:      swapper/0:    0] "0": file "dhd_msgbuf.c", line 4221
[  110.517267] I[0:      swapper/0:    0] "0": file "dhd_msgbuf.c", line 4221
[  110.517309] I[0:      swapper/0:    0] dhd_prot_rxbufpost:2074: Rxbufpost Msgbuf Not available

In-Depth Analysis

Exploiting the out of bound ringbuffer pointer

The out of bound ringbuffer pointer calculated in prot_get_ring_space can potentially lead to an out-of bound memory access in the function dhd_prot_rxbufpost, which calls the function dhd_alloc_ring_space at (1) in order to obtain a ringbuffer pointer which can hold count elements.

/* Post count no of rx buffers down to dongle */
static int BCMFASTPATH
dhd_prot_rxbufpost(dhd_pub_t *dhd, uint16 count)
{
	...
	DHD_GENERAL_LOCK(dhd, flags);
	/* Claim space for 'count' no of messages */
	// (1) get ring buffer pointer for <count> new entries
	msg_start = (void *)dhd_alloc_ring_space(dhd, ring, count, &alloced);
	DHD_GENERAL_UNLOCK(dhd, flags);

	if (msg_start == NULL) {
		DHD_INFO(("%s:%d: Rxbufpost Msgbuf Not available\n", __FUNCTION__, __LINE__));
		return -1;
	}
	/* if msg_start !=  NULL, we should have alloced space for atleast 1 item */
	ASSERT(alloced > 0);

	rxbuf_post_tmp = (uint8*)msg_start;

	/* loop through each message */
	for (i = 0; i < alloced; i++) {
		rxbuf_post = (host_rxbuf_post_t *)rxbuf_post_tmp;
		...
		/* Move rxbuf_post_tmp to next item */
		rxbuf_post_tmp = rxbuf_post_tmp + RING_LEN_ITEMS(ring);
	}
	...
	return alloced;
}

The function dhd_alloc_ring_space in turn updates the attacker controlled value of RING_READ_PTR(ring) at (2) and invokes prot_get_ring_space at (3).

static void * BCMFASTPATH
dhd_alloc_ring_space(dhd_pub_t *dhd, msgbuf_ring_t *ring, uint16 nitems, uint16 * alloced)
{
	void * ret_buf;
	uint16 r_index = 0;

	/* Alloc space for nitems in the ring */
	ret_buf = prot_get_ring_space(ring, nitems, alloced);

	if (ret_buf == NULL) {
		/* if alloc failed , invalidate cached read ptr */
		if (DMA_INDX_ENAB(dhd->dma_d2h_ring_upd_support)) {
			r_index = dhd_get_dmaed_index(dhd, H2D_DMA_READINDX, ring->idx);
			ring->ringstate->r_offset = r_index;
		} else
			// (2) RING_READ_PTR(ring) is updated here
			dhd_bus_cmn_readshared(dhd->bus, &(RING_READ_PTR(ring)),
				RING_READ_PTR, ring->idx);

		/* Try allocating once more */
		// (3) invokation of prot_get_ring_space
		ret_buf = prot_get_ring_space(ring, nitems, alloced);

		if (ret_buf == NULL) {
			DHD_INFO(("RING space not available on ring %s for %d items \n",
				ring->name, nitems));
			DHD_INFO(("write %d read %d \n\n", RING_WRITE_PTR(ring),
				RING_READ_PTR(ring)));
			return NULL;
		}
	}

	/* Return alloced space */
	return ret_buf;
}

Since the attacker controls the value of ring_avail_cnt in prot_get_ring_space at (4), the value of *alloced calculated at (5) can be equal to nitems even though there are less then nitems free entries left in the ringbuffer. Further, the comparison at (6) can cause an integer overflow if (RING_WRITE_PTR(ring) + *alloced) is greater then 0xffff.

/* Assumes only one index is updated ata time */
static void *BCMFASTPATH
prot_get_ring_space(msgbuf_ring_t *ring, uint16 nitems, uint16 * alloced)
{
	void *ret_ptr = NULL;
	uint16 ring_avail_cnt;

	ASSERT(nitems <= RING_MAX_ITEM(ring));

	// (4) ring_avail_cnt is under attacker control
	ring_avail_cnt = CHECK_WRITE_SPACE(RING_READ_PTR(ring), RING_WRITE_PTR(ring),
		RING_MAX_ITEM(ring));

	if (ring_avail_cnt == 0) {
		return NULL;
	}
	// (5) alloced will be equal to nitems
	*alloced = MIN(nitems, ring_avail_cnt);

	/* Return next available space */
	ret_ptr = (char*)HOST_RING_BASE(ring) + (RING_WRITE_PTR(ring) * RING_LEN_ITEMS(ring));

	/* Update write pointer */
	if ((RING_WRITE_PTR(ring) + *alloced) == RING_MAX_ITEM(ring))
		RING_WRITE_PTR(ring) = 0;
	// (6) integer overflow for values of *alloced > 0xffff - RING_WRITE_PTR(ring) + 1
	else if ((RING_WRITE_PTR(ring) + *alloced) < RING_MAX_ITEM(ring))
		RING_WRITE_PTR(ring) += *alloced;
	else {
		/* Should never hit this */
		ASSERT(0);
		return NULL;
	}

	return ret_ptr;
}