Date: Sat, 12 Apr 2014 16:03:38 +0300 From: Guy Yur <guyyur@gmail.com> To: FreeBSD-gnats-submit@freebsd.org Subject: arm/188510: [patch] "rtadvctl show" crashes on BeagleBone Black due to unaligned access Message-ID: <534939c2.045f0e0a.59a8.ffff808b@mx.google.com> Resent-Message-ID: <201404121310.s3CDA1sw056001@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 188510 >Category: arm >Synopsis: [patch] "rtadvctl show" crashes on BeagleBone Black due to unaligned access >Confidential: no >Severity: non-critical >Priority: medium >Responsible: freebsd-arm >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Sat Apr 12 13:10:01 UTC 2014 >Closed-Date: >Last-Modified: >Originator: Guy Yur >Release: FreeBSD 11.0-CURRENT arm.armv6 >Organization: >Environment: System: FreeBSD bbb-router.localdomain 11.0-CURRENT FreeBSD 11.0-CURRENT #0 r264364M: Sat Apr 12 14:23:46 IDT 2014 root@vm4.localdomain:/usr/obj/arm.armv6/usr/src/sys/BBB-ROUTER arm >Description: "rtadvctl show" core dumps on Bus error when run on BeagleBone Black. (gdb) bt #0 cm_pl2bin (str=<value optimized out>, cp=<value optimized out>) at /usr/src/usr.sbin/rtadvctl/../rtadvd/control.c:458 #1 0x0000a59c in action_plgeneric (action=<value optimized out>, plstr=<value optimized out>, buf=0xbfffcd6c "\001") at /usr/src/usr.sbin/rtadvctl/rtadvctl.c:264 #2 0x0000a3c8 in action_propget (argv=0xbffff2d1 "", cp=0xbfffedf0) at /usr/src/usr.sbin/rtadvctl/rtadvctl.c:285 #3 0x00009354 in action_show (argc=<value optimized out>, argv=<value optimized out>) at /usr/src/usr.sbin/rtadvctl/rtadvctl.c:432 #4 0x00009184 in main (argc=<value optimized out>, argv=0xbffff2d1) at /usr/src/usr.sbin/rtadvctl/rtadvctl.c:187 #5 0x00008fdc in __start (argc=2, argv=0xbffffb98, env=0xbffffba4, ps_strings=<value optimized out>, obj=0x2003c000, cleanup=<value optimized out>) at /usr/src/lib/csu/arm/crt1.c:115 #6 0x2001fd3c in _rtld_get_stack_prot () from /libexec/ld-elf.so.1 #7 0x2001fd3c in _rtld_get_stack_prot () from /libexec/ld-elf.so.1 Current language: auto; currently minimal disassembly: 0x0000b0c4 <cm_pl2bin+368>: str r0, [r8] info registers: ... r8 0xbfffcd87 -1073754745 ... pc 0xb0c4 45252 The protocol between rtadvd and rtadvctl writes a size_t len followed by a string for each of ifname, key and value. When ifname or key is supplied and their length is not a multiple of 4 the write of the next field size_t len will be to an unaligned address and a trap will be generated on the BeagleBone Black. >How-To-Repeat: Run "rtadvctl show" on an arm machine with trapping for unaligned access enabled. >Fix: Attached two patches with different ways to resolve the problem. 1. rtadvd_control_align.patch Round up the strings to align on sizeof(size_t). Is there a round up macro that can be used instead of explicit calculation? Requires using matching rtadvd and rtadvctl since the protocol changed. 2. rtadvd_control_packed.patch Use __packed structure access for the size_t len so byte instructions will be used to read/write the len on arm. Protocol doesn't change so compatibility between old and fixed rtadvd and rtadvctl is kept. --- rtadvd_control_align.patch begins here --- Index: usr.sbin/rtadvd/control.c =================================================================== --- usr.sbin/rtadvd/control.c (revision 264366) +++ usr.sbin/rtadvd/control.c (working copy) @@ -362,7 +362,7 @@ cm_bin2pl(char *str, struct ctrl_msg_pl *cp) } memcpy(cp->cp_ifname, p, len); cp->cp_ifname[len] = '\0'; - p += len; + p += (len + sizeof(size_t)) & ~(sizeof(size_t) - 1); } lenp = (size_t *)p; @@ -377,7 +377,7 @@ cm_bin2pl(char *str, struct ctrl_msg_pl *cp) } memcpy(cp->cp_key, p, len); cp->cp_key[len] = '\0'; - p += len; + p += (len + sizeof(size_t)) & ~(sizeof(size_t) - 1); } lenp = (size_t *)p; @@ -402,20 +402,26 @@ cm_bin2pl(char *str, struct ctrl_msg_pl *cp) size_t cm_pl2bin(char *str, struct ctrl_msg_pl *cp) { - size_t len; + size_t len, ifname_len, key_len, val_len; size_t *lenp; char *p; struct ctrl_msg_hdr *cm; len = sizeof(size_t); - if (cp->cp_ifname != NULL) - len += strlen(cp->cp_ifname); + if (cp->cp_ifname != NULL) { + ifname_len = strlen(cp->cp_ifname); + len += (ifname_len + sizeof(size_t)) & ~(sizeof(size_t) - 1); + } len += sizeof(size_t); - if (cp->cp_key != NULL) - len += strlen(cp->cp_key); + if (cp->cp_key != NULL) { + key_len = strlen(cp->cp_key); + len += (key_len + sizeof(size_t)) & ~(sizeof(size_t) - 1); + } len += sizeof(size_t); - if (cp->cp_val != NULL && cp->cp_val_len > 0) - len += cp->cp_val_len; + if (cp->cp_val != NULL && cp->cp_val_len > 0) { + val_len = cp->cp_val_len; + len += (val_len + sizeof(size_t)) & ~(sizeof(size_t) - 1); + } if (len > CM_MSG_MAXLEN - sizeof(*cm)) { syslog(LOG_DEBUG, "<%s> msg too long (len=%zu)", @@ -426,36 +432,36 @@ cm_pl2bin(char *str, struct ctrl_msg_pl *cp) memset(str, 0, len); p = str; lenp = (size_t *)p; - + if (cp->cp_ifname != NULL) { - *lenp++ = strlen(cp->cp_ifname); + *lenp++ = ifname_len; p = (char *)lenp; - memcpy(p, cp->cp_ifname, strlen(cp->cp_ifname)); - p += strlen(cp->cp_ifname); + memcpy(p, cp->cp_ifname, ifname_len); + p += (ifname_len + sizeof(size_t)) & ~(sizeof(size_t) - 1); } else { - *lenp++ = '\0'; + *lenp++ = 0; p = (char *)lenp; } lenp = (size_t *)p; if (cp->cp_key != NULL) { - *lenp++ = strlen(cp->cp_key); + *lenp++ = key_len; p = (char *)lenp; - memcpy(p, cp->cp_key, strlen(cp->cp_key)); - p += strlen(cp->cp_key); + memcpy(p, cp->cp_key, key_len); + p += (key_len + sizeof(size_t)) & ~(sizeof(size_t) - 1); } else { - *lenp++ = '\0'; + *lenp++ = 0; p = (char *)lenp; } lenp = (size_t *)p; if (cp->cp_val != NULL && cp->cp_val_len > 0) { - *lenp++ = cp->cp_val_len; + *lenp++ = val_len; p = (char *)lenp; - memcpy(p, cp->cp_val, cp->cp_val_len); - p += cp->cp_val_len; + memcpy(p, cp->cp_val, val_len); + p += (val_len + sizeof(size_t)) & ~(sizeof(size_t) - 1); } else { - *lenp++ = '\0'; + *lenp++ = 0; p = (char *)lenp; } --- rtadvd_control_align.patch ends here --- --- rtadvd_control_packed.patch begins here --- Index: usr.sbin/rtadvd/control.c =================================================================== --- usr.sbin/rtadvd/control.c (revision 264366) +++ usr.sbin/rtadvd/control.c (working copy) @@ -343,7 +343,10 @@ struct ctrl_msg_pl * cm_bin2pl(char *str, struct ctrl_msg_pl *cp) { size_t len; - size_t *lenp; + struct unaligned_len { + size_t len; + } __packed; + struct unaligned_len *lenp; char *p; memset(cp, 0, sizeof(*cp)); @@ -350,9 +353,9 @@ cm_bin2pl(char *str, struct ctrl_msg_pl *cp) p = str; - lenp = (size_t *)p; - len = *lenp++; - p = (char *)lenp; + lenp = (struct unaligned_len *)p; + len = lenp->len; + p = (char *)(lenp + 1); syslog(LOG_DEBUG, "<%s> len(ifname) = %zu", __func__, len); if (len > 0) { cp->cp_ifname = malloc(len + 1); @@ -365,9 +368,9 @@ cm_bin2pl(char *str, struct ctrl_msg_pl *cp) p += len; } - lenp = (size_t *)p; - len = *lenp++; - p = (char *)lenp; + lenp = (struct unaligned_len *)p; + len = lenp->len; + p = (char *)(lenp + 1); syslog(LOG_DEBUG, "<%s> len(key) = %zu", __func__, len); if (len > 0) { cp->cp_key = malloc(len + 1); @@ -380,9 +383,9 @@ cm_bin2pl(char *str, struct ctrl_msg_pl *cp) p += len; } - lenp = (size_t *)p; - len = *lenp++; - p = (char *)lenp; + lenp = (struct unaligned_len *)p; + len = lenp->len; + p = (char *)(lenp + 1); syslog(LOG_DEBUG, "<%s> len(val) = %zu", __func__, len); if (len > 0) { cp->cp_val = malloc(len + 1); @@ -403,7 +406,10 @@ size_t cm_pl2bin(char *str, struct ctrl_msg_pl *cp) { size_t len; - size_t *lenp; + struct unaligned_len { + size_t len; + } __packed; + struct unaligned_len *lenp; char *p; struct ctrl_msg_hdr *cm; @@ -425,38 +431,38 @@ cm_pl2bin(char *str, struct ctrl_msg_pl *cp) syslog(LOG_DEBUG, "<%s> msglen=%zu", __func__, len); memset(str, 0, len); p = str; - lenp = (size_t *)p; - + lenp = (struct unaligned_len *)p; + if (cp->cp_ifname != NULL) { - *lenp++ = strlen(cp->cp_ifname); - p = (char *)lenp; + lenp->len = strlen(cp->cp_ifname); + p = (char *)(lenp + 1); memcpy(p, cp->cp_ifname, strlen(cp->cp_ifname)); p += strlen(cp->cp_ifname); } else { - *lenp++ = '\0'; - p = (char *)lenp; + lenp->len = 0; + p = (char *)(lenp + 1); } - lenp = (size_t *)p; + lenp = (struct unaligned_len *)p; if (cp->cp_key != NULL) { - *lenp++ = strlen(cp->cp_key); - p = (char *)lenp; + lenp->len = strlen(cp->cp_key); + p = (char *)(lenp + 1); memcpy(p, cp->cp_key, strlen(cp->cp_key)); p += strlen(cp->cp_key); } else { - *lenp++ = '\0'; - p = (char *)lenp; + lenp->len = 0; + p = (char *)(lenp + 1); } - lenp = (size_t *)p; + lenp = (struct unaligned_len *)p; if (cp->cp_val != NULL && cp->cp_val_len > 0) { - *lenp++ = cp->cp_val_len; - p = (char *)lenp; + lenp->len = cp->cp_val_len; + p = (char *)(lenp + 1); memcpy(p, cp->cp_val, cp->cp_val_len); p += cp->cp_val_len; } else { - *lenp++ = '\0'; - p = (char *)lenp; + lenp->len = 0; + p = (char *)(lenp + 1); } return (len); --- rtadvd_control_packed.patch ends here --- >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?534939c2.045f0e0a.59a8.ffff808b>