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

Michael Shavit mshavit at google.com
Thu Aug 31 00:00:00 PDT 2023


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.

---

 lib/lib.c           | 39 +++++++++++++++++++++++++++++++++++++++
 lib/lib.h           |  1 +
 toys/other/devmem.c | 17 +++++++++++++----
 3 files changed, 53 insertions(+), 4 deletions(-)

diff --git a/lib/lib.c b/lib/lib.c
index c4e70dfe..315f4ef5 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -335,6 +335,45 @@ long long atolx_range(char *numstr, long long low, long long high)
   return val;
 }
 
+static unsigned long long estrtoull(char *str, char **end, int base)
+{
+  errno = 0;
+
+  return strtoull(str, end, base);
+}
+
+static unsigned long long xstrtoull(char *str, char **end, int base)
+{
+  unsigned long long l = estrtoull(str, end, base);
+
+  if (errno) perror_exit_raw(str);
+
+  return l;
+}
+
+// Unsigned version of atolx()
+unsigned long long atoullx(char *numstr)
+{
+  char *c = numstr, *suffixes="cwbkmgtpe", *end;
+  unsigned long long val;
+
+  val = xstrtoull(numstr, &c, 0);
+  if (c != numstr && *c && (end = strchr(suffixes, tolower(*c)))) {
+    int shift = end-suffixes-2;
+    ++c;
+    if (shift==-1) val *= 2;
+    else if (!shift) val *= 512;
+    else if (shift>0) {
+      if (*c && tolower(*c++)=='d') while (shift--) val *= 1000;
+      else val *= 1LL<<(shift*10);
+    }
+  }
+  while (isspace(*c)) c++;
+  if (c==numstr || *c) error_exit("not integer: %s", numstr);
+
+  return val;
+}
+
 int stridx(char *haystack, char needle)
 {
   char *off;
diff --git a/lib/lib.h b/lib/lib.h
index 6851c4aa..0c3a7d8a 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -226,6 +226,7 @@ void poke(void *ptr, long long val, unsigned size);
 struct string_list *find_in_path(char *path, char *filename);
 long long estrtol(char *str, char **end, int base);
 long long xstrtol(char *str, char **end, int base);
+unsigned long long atoullx(char *c);
 long long atolx(char *c);
 long long atolx_range(char *numstr, long long low, long long high);
 int stridx(char *haystack, char needle);
diff --git a/toys/other/devmem.c b/toys/other/devmem.c
index ced6c518..7c34b32f 100644
--- a/toys/other/devmem.c
+++ b/toys/other/devmem.c
@@ -21,9 +21,9 @@ config DEVMEM
 void devmem_main(void)
 {
   int writing = toys.optc == 3, page_size = sysconf(_SC_PAGESIZE), bytes = 4,fd;
-  unsigned long long addr = atolx(toys.optargs[0]), data = 0, map_off, map_len;
+  unsigned long long addr = atoullx(toys.optargs[0]), data = 0, map_off, map_len;
+  unsigned long long max_value = ~0ULL;
   void *map, *p;
-
   // WIDTH?
   if (toys.optc>1) {
     int i;
@@ -33,8 +33,17 @@ void devmem_main(void)
     bytes = 1<<i;
   }
 
-  // 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) {
+    // Shifting 1 left by N (and then decrementing by 1) to compute the maximum
+    // value that can fit in N bits can result in undefined behavior if N is
+    // equal to the bit length of the operand. So instead, right shift the
+    // maximum value that fits in data such that only the right-most `width`
+    // bytes are set.
+    max_value = max_value >> (8 * (sizeof(max_value) - bytes));
+    data = atoullx(toys.optargs[2]);
+    if (data > max_value)
+      error_exit("data: %llu exceeds write width: %d", data, bytes);
+  }
 
   // Map in just enough.
   fd = xopen("/dev/mem", (writing ? O_RDWR : O_RDONLY) | O_SYNC);

base-commit: 67a494e201216fb9eaa8ce685e57656036c75081
-- 
2.42.0.rc2.253.gd59a3bf2b4-goog



More information about the Toybox mailing list