Skip to content

Commit bc5a5e3

Browse files
peterhurleygregkh
authored andcommitted
n_tty: Don't wrap input buffer indices at buffer size
Wrap read_buf indices (read_head, read_tail, canon_head) at max representable value, instead of at the N_TTY_BUF_SIZE. This step is necessary to allow lockless reads of these shared variables (by updating the variables atomically). Signed-off-by: Peter Hurley <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent ce74117 commit bc5a5e3

File tree

1 file changed

+60
-51
lines changed

1 file changed

+60
-51
lines changed

drivers/tty/n_tty.c

Lines changed: 60 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ struct n_tty_data {
9696
DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE);
9797

9898
char *read_buf;
99-
int read_head;
100-
int read_tail;
99+
size_t read_head;
100+
size_t read_tail;
101101
int read_cnt;
102102
int minimum_to_wake;
103103

@@ -106,7 +106,7 @@ struct n_tty_data {
106106
unsigned int echo_cnt;
107107

108108
int canon_data;
109-
unsigned long canon_head;
109+
size_t canon_head;
110110
unsigned int canon_column;
111111

112112
struct mutex atomic_read_lock;
@@ -120,6 +120,16 @@ static inline size_t read_cnt(struct n_tty_data *ldata)
120120
return ldata->read_cnt;
121121
}
122122

123+
static inline unsigned char read_buf(struct n_tty_data *ldata, size_t i)
124+
{
125+
return ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
126+
}
127+
128+
static inline unsigned char *read_buf_addr(struct n_tty_data *ldata, size_t i)
129+
{
130+
return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
131+
}
132+
123133
static inline int tty_put_user(struct tty_struct *tty, unsigned char x,
124134
unsigned char __user *ptr)
125135
{
@@ -186,8 +196,8 @@ static void n_tty_set_room(struct tty_struct *tty)
186196
static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata)
187197
{
188198
if (read_cnt(ldata) < N_TTY_BUF_SIZE) {
189-
ldata->read_buf[ldata->read_head] = c;
190-
ldata->read_head = (ldata->read_head + 1) & (N_TTY_BUF_SIZE-1);
199+
*read_buf_addr(ldata, ldata->read_head) = c;
200+
ldata->read_head++;
191201
ldata->read_cnt++;
192202
}
193203
}
@@ -289,13 +299,10 @@ static ssize_t chars_in_buffer(struct tty_struct *tty)
289299
ssize_t n = 0;
290300

291301
raw_spin_lock_irqsave(&ldata->read_lock, flags);
292-
if (!ldata->icanon) {
302+
if (!ldata->icanon)
293303
n = read_cnt(ldata);
294-
} else if (ldata->canon_data) {
295-
n = (ldata->canon_head > ldata->read_tail) ?
296-
ldata->canon_head - ldata->read_tail :
297-
ldata->canon_head + (N_TTY_BUF_SIZE - ldata->read_tail);
298-
}
304+
else
305+
n = ldata->canon_head - ldata->read_tail;
299306
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
300307
return n;
301308
}
@@ -918,7 +925,9 @@ static void eraser(unsigned char c, struct tty_struct *tty)
918925
{
919926
struct n_tty_data *ldata = tty->disc_data;
920927
enum { ERASE, WERASE, KILL } kill_type;
921-
int head, seen_alnums, cnt;
928+
size_t head;
929+
size_t cnt;
930+
int seen_alnums;
922931
unsigned long flags;
923932

924933
/* FIXME: locking needed ? */
@@ -962,8 +971,8 @@ static void eraser(unsigned char c, struct tty_struct *tty)
962971

963972
/* erase a single possibly multibyte character */
964973
do {
965-
head = (head - 1) & (N_TTY_BUF_SIZE-1);
966-
c = ldata->read_buf[head];
974+
head--;
975+
c = read_buf(ldata, head);
967976
} while (is_continuation(c, tty) && head != ldata->canon_head);
968977

969978
/* do not partially erase */
@@ -977,7 +986,7 @@ static void eraser(unsigned char c, struct tty_struct *tty)
977986
else if (seen_alnums)
978987
break;
979988
}
980-
cnt = (ldata->read_head - head) & (N_TTY_BUF_SIZE-1);
989+
cnt = ldata->read_head - head;
981990
raw_spin_lock_irqsave(&ldata->read_lock, flags);
982991
ldata->read_head = head;
983992
ldata->read_cnt -= cnt;
@@ -991,17 +1000,16 @@ static void eraser(unsigned char c, struct tty_struct *tty)
9911000
/* if cnt > 1, output a multi-byte character */
9921001
echo_char(c, tty);
9931002
while (--cnt > 0) {
994-
head = (head+1) & (N_TTY_BUF_SIZE-1);
995-
echo_char_raw(ldata->read_buf[head],
996-
ldata);
1003+
head++;
1004+
echo_char_raw(read_buf(ldata, head), ldata);
9971005
echo_move_back_col(ldata);
9981006
}
9991007
} else if (kill_type == ERASE && !L_ECHOE(tty)) {
10001008
echo_char(ERASE_CHAR(tty), tty);
10011009
} else if (c == '\t') {
10021010
unsigned int num_chars = 0;
10031011
int after_tab = 0;
1004-
unsigned long tail = ldata->read_head;
1012+
size_t tail = ldata->read_head;
10051013

10061014
/*
10071015
* Count the columns used for characters
@@ -1011,8 +1019,8 @@ static void eraser(unsigned char c, struct tty_struct *tty)
10111019
* number of columns.
10121020
*/
10131021
while (tail != ldata->canon_head) {
1014-
tail = (tail-1) & (N_TTY_BUF_SIZE-1);
1015-
c = ldata->read_buf[tail];
1022+
tail--;
1023+
c = read_buf(ldata, tail);
10161024
if (c == '\t') {
10171025
after_tab = 1;
10181026
break;
@@ -1296,14 +1304,14 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
12961304
}
12971305
if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&
12981306
L_IEXTEN(tty)) {
1299-
unsigned long tail = ldata->canon_head;
1307+
size_t tail = ldata->canon_head;
13001308

13011309
finish_erasing(ldata);
13021310
echo_char(c, tty);
13031311
echo_char_raw('\n', ldata);
13041312
while (tail != ldata->read_head) {
1305-
echo_char(ldata->read_buf[tail], tty);
1306-
tail = (tail+1) & (N_TTY_BUF_SIZE-1);
1313+
echo_char(read_buf(ldata, tail), tty);
1314+
tail++;
13071315
}
13081316
process_echoes(tty);
13091317
return;
@@ -1356,7 +1364,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
13561364

13571365
handle_newline:
13581366
raw_spin_lock_irqsave(&ldata->read_lock, flags);
1359-
set_bit(ldata->read_head, ldata->read_flags);
1367+
set_bit(ldata->read_head & (N_TTY_BUF_SIZE - 1), ldata->read_flags);
13601368
put_tty_queue_nolock(c, ldata);
13611369
ldata->canon_head = ldata->read_head;
13621370
ldata->canon_data++;
@@ -1436,19 +1444,19 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
14361444
if (ldata->real_raw) {
14371445
raw_spin_lock_irqsave(&ldata->read_lock, cpuflags);
14381446
i = min(N_TTY_BUF_SIZE - read_cnt(ldata),
1439-
N_TTY_BUF_SIZE - ldata->read_head);
1447+
N_TTY_BUF_SIZE - (ldata->read_head & (N_TTY_BUF_SIZE - 1)));
14401448
i = min(count, i);
1441-
memcpy(ldata->read_buf + ldata->read_head, cp, i);
1442-
ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
1449+
memcpy(read_buf_addr(ldata, ldata->read_head), cp, i);
1450+
ldata->read_head += i;
14431451
ldata->read_cnt += i;
14441452
cp += i;
14451453
count -= i;
14461454

14471455
i = min(N_TTY_BUF_SIZE - read_cnt(ldata),
1448-
N_TTY_BUF_SIZE - ldata->read_head);
1456+
N_TTY_BUF_SIZE - (ldata->read_head & (N_TTY_BUF_SIZE - 1)));
14491457
i = min(count, i);
1450-
memcpy(ldata->read_buf + ldata->read_head, cp, i);
1451-
ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1);
1458+
memcpy(read_buf_addr(ldata, ldata->read_head), cp, i);
1459+
ldata->read_head += i;
14521460
ldata->read_cnt += i;
14531461
raw_spin_unlock_irqrestore(&ldata->read_lock, cpuflags);
14541462
} else {
@@ -1739,21 +1747,21 @@ static int copy_from_read_buf(struct tty_struct *tty,
17391747
size_t n;
17401748
unsigned long flags;
17411749
bool is_eof;
1750+
size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
17421751

17431752
retval = 0;
17441753
raw_spin_lock_irqsave(&ldata->read_lock, flags);
1745-
n = min(read_cnt(ldata), N_TTY_BUF_SIZE - ldata->read_tail);
1754+
n = min(read_cnt(ldata), N_TTY_BUF_SIZE - tail);
17461755
n = min(*nr, n);
17471756
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
17481757
if (n) {
1749-
retval = copy_to_user(*b, &ldata->read_buf[ldata->read_tail], n);
1758+
retval = copy_to_user(*b, read_buf_addr(ldata, tail), n);
17501759
n -= retval;
1751-
is_eof = n == 1 &&
1752-
ldata->read_buf[ldata->read_tail] == EOF_CHAR(tty);
1753-
tty_audit_add_data(tty, &ldata->read_buf[ldata->read_tail], n,
1760+
is_eof = n == 1 && read_buf(ldata, tail) == EOF_CHAR(tty);
1761+
tty_audit_add_data(tty, read_buf_addr(ldata, tail), n,
17541762
ldata->icanon);
17551763
raw_spin_lock_irqsave(&ldata->read_lock, flags);
1756-
ldata->read_tail = (ldata->read_tail + n) & (N_TTY_BUF_SIZE-1);
1764+
ldata->read_tail += n;
17571765
ldata->read_cnt -= n;
17581766
/* Turn single EOF into zero-length read */
17591767
if (L_EXTPROC(tty) && ldata->icanon && is_eof && !read_cnt(ldata))
@@ -1785,8 +1793,9 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
17851793
struct n_tty_data *ldata = tty->disc_data;
17861794
unsigned long flags;
17871795
size_t n, size, more, c;
1788-
unsigned long eol;
1789-
int ret, tail, found = 0;
1796+
size_t eol;
1797+
size_t tail;
1798+
int ret, found = 0;
17901799

17911800
/* N.B. avoid overrun if nr == 0 */
17921801

@@ -1798,10 +1807,10 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
17981807
return 0;
17991808
}
18001809

1801-
tail = ldata->read_tail;
1810+
tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
18021811
size = min_t(size_t, tail + n, N_TTY_BUF_SIZE);
18031812

1804-
n_tty_trace("%s: nr:%zu tail:%d n:%zu size:%zu\n",
1813+
n_tty_trace("%s: nr:%zu tail:%zu n:%zu size:%zu\n",
18051814
__func__, *nr, tail, n, size);
18061815

18071816
eol = find_next_bit(ldata->read_flags, size, tail);
@@ -1818,29 +1827,29 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
18181827
n = (found + eol + size) & (N_TTY_BUF_SIZE - 1);
18191828
c = n;
18201829

1821-
if (found && ldata->read_buf[eol] == __DISABLED_CHAR)
1830+
if (found && read_buf(ldata, eol) == __DISABLED_CHAR)
18221831
n--;
18231832

1824-
n_tty_trace("%s: eol:%lu found:%d n:%zu c:%zu size:%zu more:%zu\n",
1833+
n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu size:%zu more:%zu\n",
18251834
__func__, eol, found, n, c, size, more);
18261835

18271836
raw_spin_unlock_irqrestore(&ldata->read_lock, flags);
18281837

18291838
if (n > size) {
1830-
ret = copy_to_user(*b, &ldata->read_buf[tail], size);
1839+
ret = copy_to_user(*b, read_buf_addr(ldata, tail), size);
18311840
if (ret)
18321841
return -EFAULT;
18331842
ret = copy_to_user(*b + size, ldata->read_buf, n - size);
18341843
} else
1835-
ret = copy_to_user(*b, &ldata->read_buf[tail], n);
1844+
ret = copy_to_user(*b, read_buf_addr(ldata, tail), n);
18361845

18371846
if (ret)
18381847
return -EFAULT;
18391848
*b += n;
18401849
*nr -= n;
18411850

18421851
raw_spin_lock_irqsave(&ldata->read_lock, flags);
1843-
ldata->read_tail = (ldata->read_tail + c) & (N_TTY_BUF_SIZE - 1);
1852+
ldata->read_tail += c;
18441853
ldata->read_cnt -= c;
18451854
if (found) {
18461855
__clear_bit(eol, ldata->read_flags);
@@ -2230,19 +2239,19 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
22302239

22312240
static unsigned long inq_canon(struct n_tty_data *ldata)
22322241
{
2233-
int nr, head, tail;
2242+
size_t nr, head, tail;
22342243

22352244
if (!ldata->canon_data)
22362245
return 0;
22372246
head = ldata->canon_head;
22382247
tail = ldata->read_tail;
2239-
nr = (head - tail) & (N_TTY_BUF_SIZE-1);
2248+
nr = head - tail;
22402249
/* Skip EOF-chars.. */
22412250
while (head != tail) {
2242-
if (test_bit(tail, ldata->read_flags) &&
2243-
ldata->read_buf[tail] == __DISABLED_CHAR)
2251+
if (test_bit(tail & (N_TTY_BUF_SIZE - 1), ldata->read_flags) &&
2252+
read_buf(ldata, tail) == __DISABLED_CHAR)
22442253
nr--;
2245-
tail = (tail+1) & (N_TTY_BUF_SIZE-1);
2254+
tail++;
22462255
}
22472256
return nr;
22482257
}

0 commit comments

Comments
 (0)