Stop using snprintf in write_hex_to_fd (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 23 Aug 2016 21:28:56 +0000 (17:28 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 30 Aug 2016 19:34:05 +0000 (15:34 -0400)
This is somewhat of a stopgap to deal with a brutal bug.  VM guests were
seeing XMM corruption.  Eventually, we boiled down to a simple test that
even showed the host task thread's xmm state was getting corrupted.  We
noticed that printf calls (used in debugging) were clobbering the xmm
state, which stands to reason: most any glibc string or memory operation
uses xmms.  Check out strrchr, strlen, and memcmp.

It's quite legal for any function to use xmm state - we're supposed to save
it before clobbering it.  The problem comes with vcore context.  If vcore
context calls any of these functions, including snprintf, it can clobber FP
state.  Unlike other functions, vcore context is not supposed to do that.

The specific scenario was that the VMM 2LS's vcore timer tick would fire.
When we reenabled the vcore tick, we'd ultimately call write_hex_to_fd,
which would use snprintf.  That would clobber the FPU state, which was the
guest's state.

The long term fix is to either aggressively save and restore FP state in
vcore context or ensure that no glibc helpers use xmms when we're in vcore
context.  The choice will probably depend on how expensive the FP
save/restores are on x86.  Fixing the glibc helpers would be a minor pain;
we'd need our own sysdep, but we'd still want to call the xmm version
during non-vcore-context.  That also doesn't protect us from any other apps
that might try to call an xmm op during an event handler.

So in that regard, it's a stopgap.  I also like not relying on glibc in
general from vcore context, so the less snprintfs for simple things, the
better.

Rebuild glibc.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/Versions
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/plan9_sockets.c
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/sys/plan9_helpers.h

index d36b2b1..b9c4d20 100644 (file)
@@ -64,6 +64,8 @@ libc {
     get_sibling_fd;
     write_hex_to_fd;
 
+    u64_to_str;
+
     eventfd;
     eventfd_read;
     eventfd_write;
index 1ec6481..97781fe 100644 (file)
@@ -364,10 +364,44 @@ int write_hex_to_fd(int fd, uint64_t num)
 {
        int ret;
        char cmd[50];
+       char *ptr;
 
-       ret = snprintf(cmd, sizeof(cmd), "%llx", num);
-       ret = write(fd, cmd, ret);
+       ptr = u64_to_str(num, cmd, sizeof(cmd));
+       if (!ptr)
+               return -1;
+       ret = write(fd, ptr, sizeof(cmd) - (ptr - cmd));
        if (ret <= 0)
                return -1;
        return 0;
 }
+
+/* Returns a char representing the lowest 4 bits of x */
+static char num_to_nibble(unsigned int x)
+{
+       return "0123456789abcdef"[x & 0xf];
+}
+
+/* Converts num to a string, in hex, using buf as storage.  Returns a pointer to
+ * the string from within your buf, or 0 on failure. */
+char *u64_to_str(uint64_t num, char *buf, size_t len)
+{
+       char *ptr;
+       size_t nr_nibbles = sizeof(num) * 8 / 4;
+
+       /* 3: 0, x, and \0 */
+       if (len < nr_nibbles + 3)
+               return 0;
+       ptr = &buf[len - 1];
+       /* Build the string backwards */
+       *ptr = '\0';
+       for (int i = 0; i < nr_nibbles; i++) {
+               ptr--;
+               *ptr = num_to_nibble(num);
+               num >>= 4;
+       }
+       ptr--;
+       *ptr = 'x';
+       ptr--;
+       *ptr = '0';
+       return ptr;
+}
index 0155ab2..71214af 100644 (file)
@@ -80,6 +80,9 @@ extern int _sock_lookup_listen_fd(int sock_fd);
 int get_sibling_fd(int fd, const char *sibling);
 int write_hex_to_fd(int fd, uint64_t num);
 
+/* Integer to string conversion helpers, probably faster than snprintf. */
+char *u64_to_str(uint64_t num, char *buf, size_t len);
+
 extern void _syserrno(void);
 
 /* The plan9 UDP header looks like: