aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2013-12-12 17:49:14 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2013-12-29 07:32:41 -0500
commitd46cda12e8b0fa5082cb40eb4bbfcba66b603bb2 (patch)
treef142b36b885e79b0a09bd87a8217162e1eb93d97
parent70f665fe77c54740d0fa8aaad5de2181d75af15e (diff)
ARM: fix csum_tcpudp_magic() miscompilation
There is a miscompilation of csum_tcpudp_magic() due to the way we pass the asm() operands in. Fortunately, this doesn't affect the IP code, but can affect anyone who passes ntohs(udp->len) as the length argument, or protocols with more than 8 bits. The problem stems from passing 16-bit operands into an asm() - GCC makes no guarantees about what may be in the high 16-bits of such a register passed into assembly which is in the "HI" machine mode. Address this by changing the way we handle the 16-bit arguments - since accumulating the protocol and length can never overflow, we can delegate this to the compiler to perform, and then accumulate it into the checksum inside the asm(), taking account of the endian-ness via an appropriate 32-bit rotation. While we are here, also realise that there's a chance to optimise this a little: several callers from IP code pass a constant zero as the initial sum. This is wasteful - if we detect this condition, we can optimise away one instruction. Tested-by: Maxime Bizon <mbizon@freebox.fr> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--arch/arm/include/asm/checksum.h34
1 files changed, 24 insertions, 10 deletions
diff --git a/arch/arm/include/asm/checksum.h b/arch/arm/include/asm/checksum.h
index 6dcc16430868..523315115478 100644
--- a/arch/arm/include/asm/checksum.h
+++ b/arch/arm/include/asm/checksum.h
@@ -87,19 +87,33 @@ static inline __wsum
87csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len, 87csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len,
88 unsigned short proto, __wsum sum) 88 unsigned short proto, __wsum sum)
89{ 89{
90 __asm__( 90 u32 lenprot = len | proto << 16;
91 "adds %0, %1, %2 @ csum_tcpudp_nofold \n\ 91 if (__builtin_constant_p(sum) && sum == 0) {
92 adcs %0, %0, %3 \n" 92 __asm__(
93 "adds %0, %1, %2 @ csum_tcpudp_nofold0 \n\t"
93#ifdef __ARMEB__ 94#ifdef __ARMEB__
94 "adcs %0, %0, %4 \n" 95 "adcs %0, %0, %3 \n\t"
95#else 96#else
96 "adcs %0, %0, %4, lsl #8 \n" 97 "adcs %0, %0, %3, ror #8 \n\t"
97#endif 98#endif
98 "adcs %0, %0, %5 \n\ 99 "adc %0, %0, #0"
99 adc %0, %0, #0" 100 : "=&r" (sum)
100 : "=&r"(sum) 101 : "r" (daddr), "r" (saddr), "r" (lenprot)
101 : "r" (sum), "r" (daddr), "r" (saddr), "r" (len), "Ir" (htons(proto)) 102 : "cc");
102 : "cc"); 103 } else {
104 __asm__(
105 "adds %0, %1, %2 @ csum_tcpudp_nofold \n\t"
106 "adcs %0, %0, %3 \n\t"
107#ifdef __ARMEB__
108 "adcs %0, %0, %4 \n\t"
109#else
110 "adcs %0, %0, %4, ror #8 \n\t"
111#endif
112 "adc %0, %0, #0"
113 : "=&r"(sum)
114 : "r" (sum), "r" (daddr), "r" (saddr), "r" (lenprot)
115 : "cc");
116 }
103 return sum; 117 return sum;
104} 118}
105/* 119/*