[Toybox] [PATCH] devmem: Fix 8 byte wide writes

Michael Shavit mshavit at google.com
Thu Aug 31 05:26:29 PDT 2023


On Thu, Aug 31, 2023 at 6:57 PM Rob Landley <rob at landley.net> wrote:
>
> On 8/31/23 02:00, Michael Shavit wrote:
> > Replace the left-shift method of computing the maximum allowed write as
> > it may exibit undefined behavior.
> > Add and use an unsigned version of atolx to support writing any 64bit
> > value.
> >
> > * When bytes equals 8, devmem will left-shift an unsigned long long by
> >   64 bits which is undefined behavior if sizeof(unsigned long long) is
> >   64.
> > * In addition, atolx_range's 'high' parameter is a signed long long and
> >   will therefore interpret a high value of ULLONG_MAX as -1.
> > * Finally, atolx relies on strtoll under the hood, which will reject
> >   values beyond LLONG_MAX.
>
> Could you send me the test case you hit that motivated this change? (I haven't
> got a tests/devmem.test yet and am not 100% sure how I'd even set that up in
> mkroot under qemu, but I should work out something...)

I ran something like this:
# devmem 0x000000008b300000 8 0x8FFFFFFFFFFFFFFF
# devmem 0x000000008b300000 8
0x8fffffffffffffff

# devmem 0x000000008b300000 8 500k
# devmem 0x000000008b300000 8
0x0000000007d000

# devmem 0x000000008b300000 8 0xFFFFFFFFFFFFFFFF
# devmem 0x000000008b300000 8
0xffffffffffffffff

# devmem 0x000000008b300000 4 0xFFFFFFFF
# devmem 0x000000008b300000 4
0xffffffff

# devmem 0x000000008b300000  4 0x1FFFFFFFF
devmem: data: 8589934591 exceeds write width: 4

>
> > ---
> >
> >  lib/lib.c           | 39 +++++++++++++++++++++++++++++++++++++++
> >  lib/lib.h           |  1 +
> >  toys/other/devmem.c | 17 +++++++++++++----
> >  3 files changed, 53 insertions(+), 4 deletions(-)
>
> More than doubling the size of the code (47 lines total before, you added 49)
> and copying a library function into a command (with duplicate suffix list) to
> fix a problem that might just be "use sscanf("%llu", &blah) instead".
>
> Are you using the suffixes? The easy way to get full unsigned range is to drop
> the suffixes. (And it documents the 0x prefix so strtoull rather than sscanf...)

I don't, but problem is I don't know if other users of devmem have
come to expect and depend on this behavior. Are we ok potentially
breaking compatibility for a bug fix?

> Is sizeof(unsigned long long) ever _not_ 8?

IIUC, technically the C spec only guarantees that it's at least 8. But
anyways, the intent of using sizeof() was to make sure any updates of
the data/max_value types would propagate to the check.

>
> I take it you're still ok with the decimal output being signed instead of
> unsigned?

What do you mean, isn't %llx unsigned?

>
> How does this look (untested):

I can test this out in a bit :) .

>
>  devmem.c |   18 ++++++++++++++----
>  1 file changed, 14 insertions(+), 4 deletions(-)
>
> --- a/toys/other/devmem.c
> +++ b/toys/other/devmem.c
> @@ -17,11 +17,20 @@ config DEVMEM
>  #define FOR_devmem
>  #include "toys.h"
>
> +unsigned long long atollu(char *str)
> +{
> +  char *end = str;
> +  unsigned long long llu = strtoul(str, &end, 0);
> +
> +  if (*end) error_exit("bad %s", str);
> +
> +  return llu;
> +}
> +
>  void devmem_main(void)
>  {
>    int writing = toys.optc == 3, page_size = sysconf(_SC_PAGESIZE), bytes = 4,fd;
> -  unsigned long long data = 0, map_off, map_len;
> -  unsigned long addr = atolx(*toys.optargs);
> +  unsigned long long data = 0, map_off, map_len, addr = atollu(*toys.optargs);
>    void *map, *p;
>
>    // WIDTH?
> @@ -34,13 +43,14 @@ void devmem_main(void)
>    }
>
>    // DATA? Report out of range values as errors rather than truncating.
> -  if (writing) data = atolx_range(toys.optargs[2], 0, (1ULL<<(8*bytes))-1);
> +  if (writing && (data = atollu(toys.optargs[2]))>(~0LL)>>(64-8*bytes))

Shouldn't it be (~0ULL)? I think (~0LL) would make this an arithmetic
shift instead of a logical shift.

> +    error_exit("%llx>%d bytes", data, bytes);
>
>    // Map in just enough.
>    if (CFG_TOYBOX_FORK) {
>      fd = xopen("/dev/mem", (writing ? O_RDWR : O_RDONLY) | O_SYNC);
>
> -    map_off = addr & ~(page_size - 1);
> +    map_off = addr & ~(page_size - 1ULL);
>      map_len = (addr+bytes-map_off);
>      map = xmmap(0, map_len, writing ? PROT_WRITE : PROT_READ, MAP_SHARED, fd,
>          map_off);


More information about the Toybox mailing list