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

tty: hold lock across tty buffer finding and buffer filling

tty_buffer_request_room is well protected, but while after it returns,
it releases the port->lock. tty->buf.tail might be modified
by either irq handler or other threads. The patch adds more protection
by holding the lock across tty buffer finding and buffer filling.

Signed-off-by: Alek Du <alek.du@intel.com>
Signed-off-by: Xiaobing Tu <xiaobing.tu@intel.com>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Xiaobing Tu and committed by
Greg Kroah-Hartman
c56a00a1 871bdea6

+65 -20
+65 -20
drivers/tty/tty_buffer.c
··· 185 185 /* Should possibly check if this fails for the largest buffer we 186 186 have queued and recycle that ? */ 187 187 } 188 - 189 188 /** 190 - * tty_buffer_request_room - grow tty buffer if needed 189 + * __tty_buffer_request_room - grow tty buffer if needed 191 190 * @tty: tty structure 192 191 * @size: size desired 193 192 * 194 193 * Make at least size bytes of linear space available for the tty 195 194 * buffer. If we fail return the size we managed to find. 196 - * 197 - * Locking: Takes tty->buf.lock 195 + * Locking: Caller must hold tty->buf.lock 198 196 */ 199 - int tty_buffer_request_room(struct tty_struct *tty, size_t size) 197 + static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) 200 198 { 201 199 struct tty_buffer *b, *n; 202 200 int left; 203 - unsigned long flags; 204 - 205 - spin_lock_irqsave(&tty->buf.lock, flags); 206 - 207 201 /* OPTIMISATION: We could keep a per tty "zero" sized buffer to 208 202 remove this conditional if its worth it. This would be invisible 209 203 to the callers */ ··· 219 225 size = left; 220 226 } 221 227 222 - spin_unlock_irqrestore(&tty->buf.lock, flags); 223 228 return size; 229 + } 230 + 231 + 232 + /** 233 + * tty_buffer_request_room - grow tty buffer if needed 234 + * @tty: tty structure 235 + * @size: size desired 236 + * 237 + * Make at least size bytes of linear space available for the tty 238 + * buffer. If we fail return the size we managed to find. 239 + * 240 + * Locking: Takes tty->buf.lock 241 + */ 242 + int tty_buffer_request_room(struct tty_struct *tty, size_t size) 243 + { 244 + unsigned long flags; 245 + int length; 246 + 247 + spin_lock_irqsave(&tty->buf.lock, flags); 248 + length = __tty_buffer_request_room(tty, size); 249 + spin_unlock_irqrestore(&tty->buf.lock, flags); 250 + return length; 224 251 } 225 252 EXPORT_SYMBOL_GPL(tty_buffer_request_room); 226 253 ··· 264 249 int copied = 0; 265 250 do { 266 251 int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); 267 - int space = tty_buffer_request_room(tty, goal); 268 - struct tty_buffer *tb = tty->buf.tail; 252 + int space; 253 + unsigned long flags; 254 + struct tty_buffer *tb; 255 + 256 + spin_lock_irqsave(&tty->buf.lock, flags); 257 + space = __tty_buffer_request_room(tty, goal); 258 + tb = tty->buf.tail; 269 259 /* If there is no space then tb may be NULL */ 270 - if (unlikely(space == 0)) 260 + if (unlikely(space == 0)) { 261 + spin_unlock_irqrestore(&tty->buf.lock, flags); 271 262 break; 263 + } 272 264 memcpy(tb->char_buf_ptr + tb->used, chars, space); 273 265 memset(tb->flag_buf_ptr + tb->used, flag, space); 274 266 tb->used += space; 267 + spin_unlock_irqrestore(&tty->buf.lock, flags); 275 268 copied += space; 276 269 chars += space; 277 270 /* There is a small chance that we need to split the data over ··· 309 286 int copied = 0; 310 287 do { 311 288 int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); 312 - int space = tty_buffer_request_room(tty, goal); 313 - struct tty_buffer *tb = tty->buf.tail; 289 + int space; 290 + unsigned long __flags; 291 + struct tty_buffer *tb; 292 + 293 + spin_lock_irqsave(&tty->buf.lock, __flags); 294 + space = __tty_buffer_request_room(tty, goal); 295 + tb = tty->buf.tail; 314 296 /* If there is no space then tb may be NULL */ 315 - if (unlikely(space == 0)) 297 + if (unlikely(space == 0)) { 298 + spin_unlock_irqrestore(&tty->buf.lock, __flags); 316 299 break; 300 + } 317 301 memcpy(tb->char_buf_ptr + tb->used, chars, space); 318 302 memcpy(tb->flag_buf_ptr + tb->used, flags, space); 319 303 tb->used += space; 304 + spin_unlock_irqrestore(&tty->buf.lock, __flags); 320 305 copied += space; 321 306 chars += space; 322 307 flags += space; ··· 375 344 int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, 376 345 size_t size) 377 346 { 378 - int space = tty_buffer_request_room(tty, size); 347 + int space; 348 + unsigned long flags; 349 + struct tty_buffer *tb; 350 + 351 + spin_lock_irqsave(&tty->buf.lock, flags); 352 + space = __tty_buffer_request_room(tty, size); 353 + 354 + tb = tty->buf.tail; 379 355 if (likely(space)) { 380 - struct tty_buffer *tb = tty->buf.tail; 381 356 *chars = tb->char_buf_ptr + tb->used; 382 357 memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); 383 358 tb->used += space; 384 359 } 360 + spin_unlock_irqrestore(&tty->buf.lock, flags); 385 361 return space; 386 362 } 387 363 EXPORT_SYMBOL_GPL(tty_prepare_flip_string); ··· 412 374 int tty_prepare_flip_string_flags(struct tty_struct *tty, 413 375 unsigned char **chars, char **flags, size_t size) 414 376 { 415 - int space = tty_buffer_request_room(tty, size); 377 + int space; 378 + unsigned long __flags; 379 + struct tty_buffer *tb; 380 + 381 + spin_lock_irqsave(&tty->buf.lock, __flags); 382 + space = __tty_buffer_request_room(tty, size); 383 + 384 + tb = tty->buf.tail; 416 385 if (likely(space)) { 417 - struct tty_buffer *tb = tty->buf.tail; 418 386 *chars = tb->char_buf_ptr + tb->used; 419 387 *flags = tb->flag_buf_ptr + tb->used; 420 388 tb->used += space; 421 389 } 390 + spin_unlock_irqrestore(&tty->buf.lock, __flags); 422 391 return space; 423 392 } 424 393 EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);