Skip to content

Commit 700d3d6

Browse files
pm215philmd
authored andcommitted
hw/net/smc91c111: Don't allow data register access to overrun buffer
For accesses to the 91c111 data register, the address within the packet's data frame is determined by a combination of the pointer register and the offset used to access the data register, so that you can access data at effectively wider than byte width. The pointer register's pointer field is 11 bits wide, which is exactly the size to index a 2048-byte data frame. We weren't quite getting the logic right for ensuring that we end up with a pointer value to use in the s->data[][] array that isn't out of bounds: * we correctly mask when getting the initial pointer value * for the "autoincrement the pointer register" case, we correctly mask after adding 1 so that the pointer register wraps back around at the 2048 byte mark * but for the non-autoincrement case where we have to add the low 2 bits of the data register offset, we don't account for the possibility that the pointer register is 0x7ff and the addition should wrap Fix this bug by factoring out the "get the p value to use as an array index" into a function, making it use FIELD macro names rather than hard-coded constants, and having a utility function that does "add a value and wrap it" that we can use both for the "autoincrement" and "add the offset bits" codepaths. Cc: [email protected] Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2758 Signed-off-by: Peter Maydell <[email protected]> Reviewed-by: Philippe Mathieu-Daudé <[email protected]> Message-ID: <[email protected]> Signed-off-by: Philippe Mathieu-Daudé <[email protected]>
1 parent e21fe8f commit 700d3d6

File tree

1 file changed

+53
-12
lines changed

1 file changed

+53
-12
lines changed

hw/net/smc91c111.c

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "net/net.h"
1414
#include "hw/irq.h"
1515
#include "hw/net/smc91c111.h"
16+
#include "hw/registerfields.h"
1617
#include "hw/qdev-properties.h"
1718
#include "qapi/error.h"
1819
#include "qemu/log.h"
@@ -126,6 +127,13 @@ static const VMStateDescription vmstate_smc91c111 = {
126127
#define RS_TOOSHORT 0x0400
127128
#define RS_MULTICAST 0x0001
128129

130+
FIELD(PTR, PTR, 0, 11)
131+
FIELD(PTR, NOT_EMPTY, 11, 1)
132+
FIELD(PTR, RESERVED, 12, 1)
133+
FIELD(PTR, READ, 13, 1)
134+
FIELD(PTR, AUTOINCR, 14, 1)
135+
FIELD(PTR, RCV, 15, 1)
136+
129137
static inline bool packetnum_valid(int packet_num)
130138
{
131139
return packet_num >= 0 && packet_num < NUM_PACKETS;
@@ -372,6 +380,49 @@ static void smc91c111_reset(DeviceState *dev)
372380
#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
373381
#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
374382

383+
/*
384+
* The pointer register's pointer is an 11 bit value (so it exactly
385+
* indexes a 2048-byte data frame). Add the specified offset to it,
386+
* wrapping around at the 2048 byte mark, and return the resulting
387+
* wrapped value. There are flag bits in the top part of the register,
388+
* but we can ignore them here as the mask will mask them out.
389+
*/
390+
static int ptr_reg_add(smc91c111_state *s, int offset)
391+
{
392+
return (s->ptr + offset) & R_PTR_PTR_MASK;
393+
}
394+
395+
/*
396+
* For an access to the Data Register at @offset, return the
397+
* required offset into the packet's data frame. This will
398+
* perform the pointer register autoincrement if required, and
399+
* guarantees to return an in-bounds offset.
400+
*/
401+
static int data_reg_ptr(smc91c111_state *s, int offset)
402+
{
403+
int p;
404+
405+
if (s->ptr & R_PTR_AUTOINCR_MASK) {
406+
/*
407+
* Autoincrement: use the current pointer value, and
408+
* increment the pointer register's pointer field.
409+
*/
410+
p = FIELD_EX32(s->ptr, PTR, PTR);
411+
s->ptr = FIELD_DP32(s->ptr, PTR, PTR, ptr_reg_add(s, 1));
412+
} else {
413+
/*
414+
* No autoincrement: register offset determines which
415+
* byte we're addressing. Setting the pointer to the top
416+
* of the data buffer and then using the pointer wrapping
417+
* to read the bottom byte of the buffer is not something
418+
* sensible guest software will do, but the datasheet
419+
* doesn't say what the behaviour is, so we don't forbid it.
420+
*/
421+
p = ptr_reg_add(s, offset & 3);
422+
}
423+
return p;
424+
}
425+
375426
static void smc91c111_writeb(void *opaque, hwaddr offset,
376427
uint32_t value)
377428
{
@@ -518,12 +569,7 @@ static void smc91c111_writeb(void *opaque, hwaddr offset,
518569
n);
519570
return;
520571
}
521-
p = s->ptr & 0x07ff;
522-
if (s->ptr & 0x4000) {
523-
s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
524-
} else {
525-
p += (offset & 3);
526-
}
572+
p = data_reg_ptr(s, offset);
527573
s->data[n][p] = value;
528574
}
529575
return;
@@ -673,12 +719,7 @@ static uint32_t smc91c111_readb(void *opaque, hwaddr offset)
673719
n);
674720
return 0;
675721
}
676-
p = s->ptr & 0x07ff;
677-
if (s->ptr & 0x4000) {
678-
s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
679-
} else {
680-
p += (offset & 3);
681-
}
722+
p = data_reg_ptr(s, offset);
682723
return s->data[n][p];
683724
}
684725
case 12: /* Interrupt status. */

0 commit comments

Comments
 (0)