|
1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2 | 2 |
|
| 3 | +#include <kunit/visibility.h> |
3 | 4 | #include <linux/kernel.h> |
4 | 5 | #include <linux/irqflags.h> |
5 | 6 | #include <linux/string.h> |
@@ -393,25 +394,21 @@ static unsigned int to_blk_size(unsigned int size) |
393 | 394 | * Sanity checker for reserve size. The ringbuffer code assumes that a data |
394 | 395 | * block does not exceed the maximum possible size that could fit within the |
395 | 396 | * ringbuffer. This function provides that basic size check so that the |
396 | | - * assumption is safe. |
| 397 | + * assumption is safe. In particular, it guarantees that data_push_tail() will |
| 398 | + * never attempt to push the tail beyond the head. |
397 | 399 | */ |
398 | 400 | static bool data_check_size(struct prb_data_ring *data_ring, unsigned int size) |
399 | 401 | { |
400 | | - struct prb_data_block *db = NULL; |
401 | | - |
| 402 | + /* Data-less blocks take no space. */ |
402 | 403 | if (size == 0) |
403 | 404 | return true; |
404 | 405 |
|
405 | 406 | /* |
406 | | - * Ensure the alignment padded size could possibly fit in the data |
407 | | - * array. The largest possible data block must still leave room for |
408 | | - * at least the ID of the next block. |
| 407 | + * If data blocks were allowed to be larger than half the data ring |
| 408 | + * size, a wrapping data block could require more space than the full |
| 409 | + * ringbuffer. |
409 | 410 | */ |
410 | | - size = to_blk_size(size); |
411 | | - if (size > DATA_SIZE(data_ring) - sizeof(db->id)) |
412 | | - return false; |
413 | | - |
414 | | - return true; |
| 411 | + return to_blk_size(size) <= DATA_SIZE(data_ring) / 2; |
415 | 412 | } |
416 | 413 |
|
417 | 414 | /* Query the state of a descriptor. */ |
@@ -1051,8 +1048,17 @@ static char *data_alloc(struct printk_ringbuffer *rb, unsigned int size, |
1051 | 1048 | do { |
1052 | 1049 | next_lpos = get_next_lpos(data_ring, begin_lpos, size); |
1053 | 1050 |
|
1054 | | - if (!data_push_tail(rb, next_lpos - DATA_SIZE(data_ring))) { |
1055 | | - /* Failed to allocate, specify a data-less block. */ |
| 1051 | + /* |
| 1052 | + * data_check_size() prevents data block allocation that could |
| 1053 | + * cause illegal ringbuffer states. But double check that the |
| 1054 | + * used space will not be bigger than the ring buffer. Wrapped |
| 1055 | + * messages need to reserve more space, see get_next_lpos(). |
| 1056 | + * |
| 1057 | + * Specify a data-less block when the check or the allocation |
| 1058 | + * fails. |
| 1059 | + */ |
| 1060 | + if (WARN_ON_ONCE(next_lpos - begin_lpos > DATA_SIZE(data_ring)) || |
| 1061 | + !data_push_tail(rb, next_lpos - DATA_SIZE(data_ring))) { |
1056 | 1062 | blk_lpos->begin = FAILED_LPOS; |
1057 | 1063 | blk_lpos->next = FAILED_LPOS; |
1058 | 1064 | return NULL; |
@@ -1140,8 +1146,18 @@ static char *data_realloc(struct printk_ringbuffer *rb, unsigned int size, |
1140 | 1146 | return &blk->data[0]; |
1141 | 1147 | } |
1142 | 1148 |
|
1143 | | - if (!data_push_tail(rb, next_lpos - DATA_SIZE(data_ring))) |
| 1149 | + /* |
| 1150 | + * data_check_size() prevents data block reallocation that could |
| 1151 | + * cause illegal ringbuffer states. But double check that the |
| 1152 | + * new used space will not be bigger than the ring buffer. Wrapped |
| 1153 | + * messages need to reserve more space, see get_next_lpos(). |
| 1154 | + * |
| 1155 | + * Specify failure when the check or the allocation fails. |
| 1156 | + */ |
| 1157 | + if (WARN_ON_ONCE(next_lpos - blk_lpos->begin > DATA_SIZE(data_ring)) || |
| 1158 | + !data_push_tail(rb, next_lpos - DATA_SIZE(data_ring))) { |
1144 | 1159 | return NULL; |
| 1160 | + } |
1145 | 1161 |
|
1146 | 1162 | /* The memory barrier involvement is the same as data_alloc:A. */ |
1147 | 1163 | if (!atomic_long_try_cmpxchg(&data_ring->head_lpos, &head_lpos, |
@@ -1685,6 +1701,7 @@ bool prb_reserve(struct prb_reserved_entry *e, struct printk_ringbuffer *rb, |
1685 | 1701 | memset(r, 0, sizeof(*r)); |
1686 | 1702 | return false; |
1687 | 1703 | } |
| 1704 | +EXPORT_SYMBOL_IF_KUNIT(prb_reserve); |
1688 | 1705 |
|
1689 | 1706 | /* Commit the data (possibly finalizing it) and restore interrupts. */ |
1690 | 1707 | static void _prb_commit(struct prb_reserved_entry *e, unsigned long state_val) |
@@ -1759,6 +1776,7 @@ void prb_commit(struct prb_reserved_entry *e) |
1759 | 1776 | if (head_id != e->id) |
1760 | 1777 | desc_make_final(e->rb, e->id); |
1761 | 1778 | } |
| 1779 | +EXPORT_SYMBOL_IF_KUNIT(prb_commit); |
1762 | 1780 |
|
1763 | 1781 | /** |
1764 | 1782 | * prb_final_commit() - Commit and finalize (previously reserved) data to |
@@ -2184,6 +2202,7 @@ bool prb_read_valid(struct printk_ringbuffer *rb, u64 seq, |
2184 | 2202 | { |
2185 | 2203 | return _prb_read_valid(rb, &seq, r, NULL); |
2186 | 2204 | } |
| 2205 | +EXPORT_SYMBOL_IF_KUNIT(prb_read_valid); |
2187 | 2206 |
|
2188 | 2207 | /** |
2189 | 2208 | * prb_read_valid_info() - Non-blocking read of meta data for a requested |
@@ -2333,6 +2352,7 @@ void prb_init(struct printk_ringbuffer *rb, |
2333 | 2352 | infos[0].seq = -(u64)_DESCS_COUNT(descbits); |
2334 | 2353 | infos[_DESCS_COUNT(descbits) - 1].seq = 0; |
2335 | 2354 | } |
| 2355 | +EXPORT_SYMBOL_IF_KUNIT(prb_init); |
2336 | 2356 |
|
2337 | 2357 | /** |
2338 | 2358 | * prb_record_text_space() - Query the full actual used ringbuffer space for |
|
0 commit comments