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

add barriers to buffer_uptodate and set_buffer_uptodate

Let's have a look at this piece of code in __bread_slow:

get_bh(bh);
bh->b_end_io = end_buffer_read_sync;
submit_bh(REQ_OP_READ, 0, bh);
wait_on_buffer(bh);
if (buffer_uptodate(bh))
return bh;

Neither wait_on_buffer nor buffer_uptodate contain any memory barrier.
Consequently, if someone calls sb_bread and then reads the buffer data,
the read of buffer data may be executed before wait_on_buffer(bh) on
architectures with weak memory ordering and it may return invalid data.

Fix this bug by adding a memory barrier to set_buffer_uptodate and an
acquire barrier to buffer_uptodate (in a similar way as
folio_test_uptodate and folio_mark_uptodate).

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Reviewed-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: stable@vger.kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Mikulas Patocka and committed by
Linus Torvalds
d4252071 e394ff83

+24 -1
+24 -1
include/linux/buffer_head.h
··· 118 118 * of the form "mark_buffer_foo()". These are higher-level functions which 119 119 * do something in addition to setting a b_state bit. 120 120 */ 121 - BUFFER_FNS(Uptodate, uptodate) 122 121 BUFFER_FNS(Dirty, dirty) 123 122 TAS_BUFFER_FNS(Dirty, dirty) 124 123 BUFFER_FNS(Lock, locked) ··· 134 135 BUFFER_FNS(Meta, meta) 135 136 BUFFER_FNS(Prio, prio) 136 137 BUFFER_FNS(Defer_Completion, defer_completion) 138 + 139 + static __always_inline void set_buffer_uptodate(struct buffer_head *bh) 140 + { 141 + /* 142 + * make it consistent with folio_mark_uptodate 143 + * pairs with smp_load_acquire in buffer_uptodate 144 + */ 145 + smp_mb__before_atomic(); 146 + set_bit(BH_Uptodate, &bh->b_state); 147 + } 148 + 149 + static __always_inline void clear_buffer_uptodate(struct buffer_head *bh) 150 + { 151 + clear_bit(BH_Uptodate, &bh->b_state); 152 + } 153 + 154 + static __always_inline int buffer_uptodate(const struct buffer_head *bh) 155 + { 156 + /* 157 + * make it consistent with folio_test_uptodate 158 + * pairs with smp_mb__before_atomic in set_buffer_uptodate 159 + */ 160 + return (smp_load_acquire(&bh->b_state) & (1UL << BH_Uptodate)) != 0; 161 + } 137 162 138 163 #define bh_offset(bh) ((unsigned long)(bh)->b_data & ~PAGE_MASK) 139 164