Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+24 -10
+24 -10
arch/arm/include/asm/checksum.h
··· 87 87 csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len, 88 88 unsigned short proto, __wsum sum) 89 89 { 90 - __asm__( 91 - "adds %0, %1, %2 @ csum_tcpudp_nofold \n\ 92 - adcs %0, %0, %3 \n" 90 + u32 lenprot = len | proto << 16; 91 + if (__builtin_constant_p(sum) && sum == 0) { 92 + __asm__( 93 + "adds %0, %1, %2 @ csum_tcpudp_nofold0 \n\t" 93 94 #ifdef __ARMEB__ 94 - "adcs %0, %0, %4 \n" 95 + "adcs %0, %0, %3 \n\t" 95 96 #else 96 - "adcs %0, %0, %4, lsl #8 \n" 97 + "adcs %0, %0, %3, ror #8 \n\t" 97 98 #endif 98 - "adcs %0, %0, %5 \n\ 99 - adc %0, %0, #0" 100 - : "=&r"(sum) 101 - : "r" (sum), "r" (daddr), "r" (saddr), "r" (len), "Ir" (htons(proto)) 102 - : "cc"); 99 + "adc %0, %0, #0" 100 + : "=&r" (sum) 101 + : "r" (daddr), "r" (saddr), "r" (lenprot) 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 117 return sum; 104 118 } 105 119 /*