Skip to content

Commit 3a0ed3e

Browse files
deepa-hubdavem330
authored andcommitted
sock: Make sock->sk_stamp thread-safe
Al Viro mentioned (Message-ID <[email protected]>) that there is probably a race condition lurking in accesses of sk_stamp on 32-bit machines. sock->sk_stamp is of type ktime_t which is always an s64. On a 32 bit architecture, we might run into situations of unsafe access as the access to the field becomes non atomic. Use seqlocks for synchronization. This allows us to avoid using spinlocks for readers as readers do not need mutual exclusion. Another approach to solve this is to require sk_lock for all modifications of the timestamps. The current approach allows for timestamps to have their own lock: sk_stamp_lock. This allows for the patch to not compete with already existing critical sections, and side effects are limited to the paths in the patch. The addition of the new field maintains the data locality optimizations from commit 9115e8c ("net: reorganize struct sock for better data locality") Note that all the instances of the sk_stamp accesses are either through the ioctl or the syscall recvmsg. Signed-off-by: Deepa Dinamani <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 756af9c commit 3a0ed3e

File tree

4 files changed

+55
-15
lines changed

4 files changed

+55
-15
lines changed

include/net/sock.h

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ struct sock_common {
298298
* @sk_filter: socket filtering instructions
299299
* @sk_timer: sock cleanup timer
300300
* @sk_stamp: time stamp of last packet received
301+
* @sk_stamp_seq: lock for accessing sk_stamp on 32 bit architectures only
301302
* @sk_tsflags: SO_TIMESTAMPING socket options
302303
* @sk_tskey: counter to disambiguate concurrent tstamp requests
303304
* @sk_zckey: counter to order MSG_ZEROCOPY notifications
@@ -474,6 +475,9 @@ struct sock {
474475
const struct cred *sk_peer_cred;
475476
long sk_rcvtimeo;
476477
ktime_t sk_stamp;
478+
#if BITS_PER_LONG==32
479+
seqlock_t sk_stamp_seq;
480+
#endif
477481
u16 sk_tsflags;
478482
u8 sk_shutdown;
479483
u32 sk_tskey;
@@ -2297,6 +2301,34 @@ static inline void sk_drops_add(struct sock *sk, const struct sk_buff *skb)
22972301
atomic_add(segs, &sk->sk_drops);
22982302
}
22992303

2304+
static inline ktime_t sock_read_timestamp(struct sock *sk)
2305+
{
2306+
#if BITS_PER_LONG==32
2307+
unsigned int seq;
2308+
ktime_t kt;
2309+
2310+
do {
2311+
seq = read_seqbegin(&sk->sk_stamp_seq);
2312+
kt = sk->sk_stamp;
2313+
} while (read_seqretry(&sk->sk_stamp_seq, seq));
2314+
2315+
return kt;
2316+
#else
2317+
return sk->sk_stamp;
2318+
#endif
2319+
}
2320+
2321+
static inline void sock_write_timestamp(struct sock *sk, ktime_t kt)
2322+
{
2323+
#if BITS_PER_LONG==32
2324+
write_seqlock(&sk->sk_stamp_seq);
2325+
sk->sk_stamp = kt;
2326+
write_sequnlock(&sk->sk_stamp_seq);
2327+
#else
2328+
sk->sk_stamp = kt;
2329+
#endif
2330+
}
2331+
23002332
void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
23012333
struct sk_buff *skb);
23022334
void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk,
@@ -2321,7 +2353,7 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
23212353
(sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE)))
23222354
__sock_recv_timestamp(msg, sk, skb);
23232355
else
2324-
sk->sk_stamp = kt;
2356+
sock_write_timestamp(sk, kt);
23252357

23262358
if (sock_flag(sk, SOCK_WIFI_STATUS) && skb->wifi_acked_valid)
23272359
__sock_recv_wifi_status(msg, sk, skb);
@@ -2342,9 +2374,9 @@ static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk,
23422374
if (sk->sk_flags & FLAGS_TS_OR_DROPS || sk->sk_tsflags & TSFLAGS_ANY)
23432375
__sock_recv_ts_and_drops(msg, sk, skb);
23442376
else if (unlikely(sock_flag(sk, SOCK_TIMESTAMP)))
2345-
sk->sk_stamp = skb->tstamp;
2377+
sock_write_timestamp(sk, skb->tstamp);
23462378
else if (unlikely(sk->sk_stamp == SK_DEFAULT_STAMP))
2347-
sk->sk_stamp = 0;
2379+
sock_write_timestamp(sk, 0);
23482380
}
23492381

23502382
void __sock_tx_timestamp(__u16 tsflags, __u8 *tx_flags);

net/compat.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -467,12 +467,14 @@ int compat_sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp)
467467
ctv = (struct compat_timeval __user *) userstamp;
468468
err = -ENOENT;
469469
sock_enable_timestamp(sk, SOCK_TIMESTAMP);
470-
tv = ktime_to_timeval(sk->sk_stamp);
470+
tv = ktime_to_timeval(sock_read_timestamp(sk));
471+
471472
if (tv.tv_sec == -1)
472473
return err;
473474
if (tv.tv_sec == 0) {
474-
sk->sk_stamp = ktime_get_real();
475-
tv = ktime_to_timeval(sk->sk_stamp);
475+
ktime_t kt = ktime_get_real();
476+
sock_write_timestamp(sk, kt);
477+
tv = ktime_to_timeval(kt);
476478
}
477479
err = 0;
478480
if (put_user(tv.tv_sec, &ctv->tv_sec) ||
@@ -494,12 +496,13 @@ int compat_sock_get_timestampns(struct sock *sk, struct timespec __user *usersta
494496
ctv = (struct compat_timespec __user *) userstamp;
495497
err = -ENOENT;
496498
sock_enable_timestamp(sk, SOCK_TIMESTAMP);
497-
ts = ktime_to_timespec(sk->sk_stamp);
499+
ts = ktime_to_timespec(sock_read_timestamp(sk));
498500
if (ts.tv_sec == -1)
499501
return err;
500502
if (ts.tv_sec == 0) {
501-
sk->sk_stamp = ktime_get_real();
502-
ts = ktime_to_timespec(sk->sk_stamp);
503+
ktime_t kt = ktime_get_real();
504+
sock_write_timestamp(sk, kt);
505+
ts = ktime_to_timespec(kt);
503506
}
504507
err = 0;
505508
if (put_user(ts.tv_sec, &ctv->tv_sec) ||

net/core/sock.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2751,6 +2751,9 @@ void sock_init_data(struct socket *sock, struct sock *sk)
27512751
sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
27522752

27532753
sk->sk_stamp = SK_DEFAULT_STAMP;
2754+
#if BITS_PER_LONG==32
2755+
seqlock_init(&sk->sk_stamp_seq);
2756+
#endif
27542757
atomic_set(&sk->sk_zckey, 0);
27552758

27562759
#ifdef CONFIG_NET_RX_BUSY_POLL
@@ -2850,12 +2853,13 @@ int sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp)
28502853
struct timeval tv;
28512854

28522855
sock_enable_timestamp(sk, SOCK_TIMESTAMP);
2853-
tv = ktime_to_timeval(sk->sk_stamp);
2856+
tv = ktime_to_timeval(sock_read_timestamp(sk));
28542857
if (tv.tv_sec == -1)
28552858
return -ENOENT;
28562859
if (tv.tv_sec == 0) {
2857-
sk->sk_stamp = ktime_get_real();
2858-
tv = ktime_to_timeval(sk->sk_stamp);
2860+
ktime_t kt = ktime_get_real();
2861+
sock_write_timestamp(sk, kt);
2862+
tv = ktime_to_timeval(kt);
28592863
}
28602864
return copy_to_user(userstamp, &tv, sizeof(tv)) ? -EFAULT : 0;
28612865
}
@@ -2866,11 +2870,12 @@ int sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp)
28662870
struct timespec ts;
28672871

28682872
sock_enable_timestamp(sk, SOCK_TIMESTAMP);
2869-
ts = ktime_to_timespec(sk->sk_stamp);
2873+
ts = ktime_to_timespec(sock_read_timestamp(sk));
28702874
if (ts.tv_sec == -1)
28712875
return -ENOENT;
28722876
if (ts.tv_sec == 0) {
2873-
sk->sk_stamp = ktime_get_real();
2877+
ktime_t kt = ktime_get_real();
2878+
sock_write_timestamp(sk, kt);
28742879
ts = ktime_to_timespec(sk->sk_stamp);
28752880
}
28762881
return copy_to_user(userstamp, &ts, sizeof(ts)) ? -EFAULT : 0;

net/sunrpc/svcsock.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
549549
/* Don't enable netstamp, sunrpc doesn't
550550
need that much accuracy */
551551
}
552-
svsk->sk_sk->sk_stamp = skb->tstamp;
552+
sock_write_timestamp(svsk->sk_sk, skb->tstamp);
553553
set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); /* there may be more data... */
554554

555555
len = skb->len;

0 commit comments

Comments
 (0)