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

net/smc: fix application data exception

There is a certain probability that following
exceptions will occur in the wrk benchmark test:

Running 10s test @ http://11.213.45.6:80
8 threads and 64 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.72ms 13.94ms 245.33ms 94.17%
Req/Sec 1.96k 713.67 5.41k 75.16%
155262 requests in 10.10s, 23.10MB read
Non-2xx or 3xx responses: 3

We will find that the error is HTTP 400 error, which is a serious
exception in our test, which means the application data was
corrupted.

Consider the following scenarios:

CPU0 CPU1

buf_desc->used = 0;
cmpxchg(buf_desc->used, 0, 1)
deal_with(buf_desc)

memset(buf_desc->cpu_addr,0);

This will cause the data received by a victim connection to be cleared,
thus triggering an HTTP 400 error in the server.

This patch exchange the order between clear used and memset, add
barrier to ensure memory consistency.

Fixes: 1c5526968e27 ("net/smc: Clear memory when release and reuse buffer")
Signed-off-by: D. Wythe <alibuda@linux.alibaba.com>
Reviewed-by: Wenjia Zhang <wenjia@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

D. Wythe and committed by
David S. Miller
475f9ff6 e40b801b

+8 -9
+8 -9
net/smc/smc_core.c
··· 1120 1120 1121 1121 smc_buf_free(lgr, is_rmb, buf_desc); 1122 1122 } else { 1123 - buf_desc->used = 0; 1124 - memset(buf_desc->cpu_addr, 0, buf_desc->len); 1123 + /* memzero_explicit provides potential memory barrier semantics */ 1124 + memzero_explicit(buf_desc->cpu_addr, buf_desc->len); 1125 + WRITE_ONCE(buf_desc->used, 0); 1125 1126 } 1126 1127 } 1127 1128 ··· 1133 1132 if (!lgr->is_smcd && conn->sndbuf_desc->is_vm) { 1134 1133 smcr_buf_unuse(conn->sndbuf_desc, false, lgr); 1135 1134 } else { 1136 - conn->sndbuf_desc->used = 0; 1137 - memset(conn->sndbuf_desc->cpu_addr, 0, 1138 - conn->sndbuf_desc->len); 1135 + memzero_explicit(conn->sndbuf_desc->cpu_addr, conn->sndbuf_desc->len); 1136 + WRITE_ONCE(conn->sndbuf_desc->used, 0); 1139 1137 } 1140 1138 } 1141 1139 if (conn->rmb_desc) { 1142 1140 if (!lgr->is_smcd) { 1143 1141 smcr_buf_unuse(conn->rmb_desc, true, lgr); 1144 1142 } else { 1145 - conn->rmb_desc->used = 0; 1146 - memset(conn->rmb_desc->cpu_addr, 0, 1147 - conn->rmb_desc->len + 1148 - sizeof(struct smcd_cdc_msg)); 1143 + memzero_explicit(conn->rmb_desc->cpu_addr, 1144 + conn->rmb_desc->len + sizeof(struct smcd_cdc_msg)); 1145 + WRITE_ONCE(conn->rmb_desc->used, 0); 1149 1146 } 1150 1147 } 1151 1148 }