Skip to content

Commit 48ad650

Browse files
committed
Merge: pipe: wakeup wr_wait after setting max_usage
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/4269 Upstream Status: linux.git JIRA: https://issues.redhat.com/browse/RHEL-37087 CVE: CVE-2023-52672 Signed-off-by: Brian Foster <bfoster@redhat.com> Approved-by: Marc Milgram <mmilgram@redhat.com> Approved-by: John B. Wyatt IV <jwyatt@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: Rado Vrbovsky <rvrbovsk@redhat.com>
2 parents 2b7f18e + ab9b07d commit 48ad650

File tree

2 files changed

+61
-36
lines changed

2 files changed

+61
-36
lines changed

fs/pipe.c

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,36 @@ static inline bool pipe_readable(const struct pipe_inode_info *pipe)
227227
return !pipe_empty(head, tail) || !writers;
228228
}
229229

230+
static inline unsigned int pipe_update_tail(struct pipe_inode_info *pipe,
231+
struct pipe_buffer *buf,
232+
unsigned int tail)
233+
{
234+
pipe_buf_release(pipe, buf);
235+
236+
/*
237+
* If the pipe has a watch_queue, we need additional protection
238+
* by the spinlock because notifications get posted with only
239+
* this spinlock, no mutex
240+
*/
241+
if (pipe_has_watch_queue(pipe)) {
242+
spin_lock_irq(&pipe->rd_wait.lock);
243+
#ifdef CONFIG_WATCH_QUEUE
244+
if (buf->flags & PIPE_BUF_FLAG_LOSS)
245+
pipe->note_loss = true;
246+
#endif
247+
pipe->tail = ++tail;
248+
spin_unlock_irq(&pipe->rd_wait.lock);
249+
return tail;
250+
}
251+
252+
/*
253+
* Without a watch_queue, we can simply increment the tail
254+
* without the spinlock - the mutex is enough.
255+
*/
256+
pipe->tail = ++tail;
257+
return tail;
258+
}
259+
230260
static ssize_t
231261
pipe_read(struct kiocb *iocb, struct iov_iter *to)
232262
{
@@ -320,17 +350,8 @@ pipe_read(struct kiocb *iocb, struct iov_iter *to)
320350
buf->len = 0;
321351
}
322352

323-
if (!buf->len) {
324-
pipe_buf_release(pipe, buf);
325-
spin_lock_irq(&pipe->rd_wait.lock);
326-
#ifdef CONFIG_WATCH_QUEUE
327-
if (buf->flags & PIPE_BUF_FLAG_LOSS)
328-
pipe->note_loss = true;
329-
#endif
330-
tail++;
331-
pipe->tail = tail;
332-
spin_unlock_irq(&pipe->rd_wait.lock);
333-
}
353+
if (!buf->len)
354+
tail = pipe_update_tail(pipe, buf, tail);
334355
total_len -= chars;
335356
if (!total_len)
336357
break; /* common path: read succeeded */
@@ -437,12 +458,10 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
437458
goto out;
438459
}
439460

440-
#ifdef CONFIG_WATCH_QUEUE
441-
if (pipe->watch_queue) {
461+
if (pipe_has_watch_queue(pipe)) {
442462
ret = -EXDEV;
443463
goto out;
444464
}
445-
#endif
446465

447466
/*
448467
* If it wasn't empty we try to merge new data into
@@ -507,16 +526,7 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
507526
* it, either the reader will consume it or it'll still
508527
* be there for the next write.
509528
*/
510-
spin_lock_irq(&pipe->rd_wait.lock);
511-
512-
head = pipe->head;
513-
if (pipe_full(head, pipe->tail, pipe->max_usage)) {
514-
spin_unlock_irq(&pipe->rd_wait.lock);
515-
continue;
516-
}
517-
518529
pipe->head = head + 1;
519-
spin_unlock_irq(&pipe->rd_wait.lock);
520530

521531
/* Insert it into the buffer array */
522532
buf = &pipe->bufs[head & mask];
@@ -655,7 +665,7 @@ pipe_poll(struct file *filp, poll_table *wait)
655665
unsigned int head, tail;
656666

657667
/* Epoll has some historical nasty semantics, this enables them */
658-
pipe->poll_usage = 1;
668+
WRITE_ONCE(pipe->poll_usage, true);
659669

660670
/*
661671
* Reading pipe state only -- no need for acquiring the semaphore.
@@ -1308,6 +1318,11 @@ int pipe_resize_ring(struct pipe_inode_info *pipe, unsigned int nr_slots)
13081318
pipe->tail = tail;
13091319
pipe->head = head;
13101320

1321+
if (!pipe_has_watch_queue(pipe)) {
1322+
pipe->max_usage = nr_slots;
1323+
pipe->nr_accounted = nr_slots;
1324+
}
1325+
13111326
spin_unlock_irq(&pipe->rd_wait.lock);
13121327

13131328
/* This might have made more room for writers */
@@ -1325,10 +1340,8 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg)
13251340
unsigned int nr_slots, size;
13261341
long ret = 0;
13271342

1328-
#ifdef CONFIG_WATCH_QUEUE
1329-
if (pipe->watch_queue)
1343+
if (pipe_has_watch_queue(pipe))
13301344
return -EBUSY;
1331-
#endif
13321345

13331346
size = round_pipe_size(arg);
13341347
nr_slots = size >> PAGE_SHIFT;
@@ -1361,8 +1374,6 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg)
13611374
if (ret < 0)
13621375
goto out_revert_acct;
13631376

1364-
pipe->max_usage = nr_slots;
1365-
pipe->nr_accounted = nr_slots;
13661377
return pipe->max_usage * PAGE_SIZE;
13671378

13681379
out_revert_acct:
@@ -1380,10 +1391,8 @@ struct pipe_inode_info *get_pipe_info(struct file *file, bool for_splice)
13801391

13811392
if (file->f_op != &pipefifo_fops || !pipe)
13821393
return NULL;
1383-
#ifdef CONFIG_WATCH_QUEUE
1384-
if (for_splice && pipe->watch_queue)
1394+
if (for_splice && pipe_has_watch_queue(pipe))
13851395
return NULL;
1386-
#endif
13871396
return pipe;
13881397
}
13891398

include/linux/pipe_fs_i.h

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,16 @@ struct pipe_inode_info {
6262
unsigned int tail;
6363
unsigned int max_usage;
6464
unsigned int ring_size;
65-
#ifdef CONFIG_WATCH_QUEUE
66-
bool note_loss;
67-
#endif
6865
unsigned int nr_accounted;
6966
unsigned int readers;
7067
unsigned int writers;
7168
unsigned int files;
7269
unsigned int r_counter;
7370
unsigned int w_counter;
74-
unsigned int poll_usage;
71+
bool poll_usage;
72+
#ifdef CONFIG_WATCH_QUEUE
73+
bool note_loss;
74+
#endif
7575
struct page *tmp_page;
7676
struct fasync_struct *fasync_readers;
7777
struct fasync_struct *fasync_writers;
@@ -124,6 +124,22 @@ struct pipe_buf_operations {
124124
bool (*get)(struct pipe_inode_info *, struct pipe_buffer *);
125125
};
126126

127+
/**
128+
* pipe_has_watch_queue - Check whether the pipe is a watch_queue,
129+
* i.e. it was created with O_NOTIFICATION_PIPE
130+
* @pipe: The pipe to check
131+
*
132+
* Return: true if pipe is a watch queue, false otherwise.
133+
*/
134+
static inline bool pipe_has_watch_queue(const struct pipe_inode_info *pipe)
135+
{
136+
#ifdef CONFIG_WATCH_QUEUE
137+
return pipe->watch_queue != NULL;
138+
#else
139+
return false;
140+
#endif
141+
}
142+
127143
/**
128144
* pipe_empty - Return true if the pipe is empty
129145
* @head: The pipe ring head pointer

0 commit comments

Comments
 (0)