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

bcachefs: Add safe versions of varint encode/decode

This adds safe versions of bch2_varint_(encode|decode) that don't read
or write past the end of the buffer, or varint being encoded.

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>

authored by

Kent Overstreet and committed by
Kent Overstreet
8d344587 2e655e6d

+80 -6
+2 -2
fs/bcachefs/alloc_background.c
··· 130 130 131 131 #define x(_name, _bits) \ 132 132 if (fieldnr < a.v->nr_fields) { \ 133 - ret = bch2_varint_decode(in, end, &v); \ 133 + ret = bch2_varint_decode_fast(in, end, &v); \ 134 134 if (ret < 0) \ 135 135 return ret; \ 136 136 in += ret; \ ··· 166 166 nr_fields++; \ 167 167 \ 168 168 if (src._name) { \ 169 - out += bch2_varint_encode(out, src._name); \ 169 + out += bch2_varint_encode_fast(out, src._name); \ 170 170 \ 171 171 last_nonzero_field = out; \ 172 172 last_nonzero_fieldnr = nr_fields; \
+3 -3
fs/bcachefs/inode.c
··· 137 137 nr_fields++; \ 138 138 \ 139 139 if (inode->_name) { \ 140 - ret = bch2_varint_encode(out, inode->_name); \ 140 + ret = bch2_varint_encode_fast(out, inode->_name); \ 141 141 out += ret; \ 142 142 \ 143 143 if (_bits > 64) \ ··· 246 246 247 247 #define x(_name, _bits) \ 248 248 if (fieldnr < INODE_NR_FIELDS(inode.v)) { \ 249 - ret = bch2_varint_decode(in, end, &v[0]); \ 249 + ret = bch2_varint_decode_fast(in, end, &v[0]); \ 250 250 if (ret < 0) \ 251 251 return ret; \ 252 252 in += ret; \ 253 253 \ 254 254 if (_bits > 64) { \ 255 - ret = bch2_varint_decode(in, end, &v[1]); \ 255 + ret = bch2_varint_decode_fast(in, end, &v[1]); \ 256 256 if (ret < 0) \ 257 257 return ret; \ 258 258 in += ret; \
+72 -1
fs/bcachefs/varint.c
··· 2 2 3 3 #include <linux/bitops.h> 4 4 #include <linux/math.h> 5 + #include <linux/string.h> 5 6 #include <asm/unaligned.h> 6 7 7 8 #include "varint.h" 8 9 10 + /** 11 + * bch2_varint_encode - encode a variable length integer 12 + * @out - destination to encode to 13 + * @v - unsigned integer to encode 14 + * 15 + * Returns the size in bytes of the encoded integer - at most 9 bytes 16 + */ 9 17 int bch2_varint_encode(u8 *out, u64 v) 18 + { 19 + unsigned bits = fls64(v|1); 20 + unsigned bytes = DIV_ROUND_UP(bits, 7); 21 + 22 + if (likely(bytes < 9)) { 23 + v <<= bytes; 24 + v |= ~(~0 << (bytes - 1)); 25 + v = cpu_to_le64(v); 26 + memcpy(out, &v, bytes); 27 + } else { 28 + *out++ = 255; 29 + bytes = 9; 30 + put_unaligned_le64(v, out); 31 + } 32 + 33 + return bytes; 34 + } 35 + 36 + /** 37 + * bch2_varint_decode - encode a variable length integer 38 + * @in - varint to decode 39 + * @end - end of buffer to decode from 40 + * @out - on success, decoded integer 41 + * 42 + * Returns the size in bytes of the decoded integer - or -1 on failure (would 43 + * have read past the end of the buffer) 44 + */ 45 + int bch2_varint_decode(const u8 *in, const u8 *end, u64 *out) 46 + { 47 + unsigned bytes = likely(in < end) 48 + ? ffz(*in & 255) + 1 49 + : 1; 50 + u64 v; 51 + 52 + if (unlikely(in + bytes > end)) 53 + return -1; 54 + 55 + if (likely(bytes < 9)) { 56 + v = 0; 57 + memcpy(&v, in, bytes); 58 + v = le64_to_cpu(v); 59 + v >>= bytes; 60 + } else { 61 + v = get_unaligned_le64(++in); 62 + } 63 + 64 + *out = v; 65 + return bytes; 66 + } 67 + 68 + /** 69 + * bch2_varint_encode_fast - fast version of bch2_varint_encode 70 + * 71 + * This version assumes it's always safe to write 8 bytes to @out, even if the 72 + * encoded integer would be smaller. 73 + */ 74 + int bch2_varint_encode_fast(u8 *out, u64 v) 10 75 { 11 76 unsigned bits = fls64(v|1); 12 77 unsigned bytes = DIV_ROUND_UP(bits, 7); ··· 88 23 return bytes; 89 24 } 90 25 91 - int bch2_varint_decode(const u8 *in, const u8 *end, u64 *out) 26 + /** 27 + * bch2_varint_decode_fast - fast version of bch2_varint_decode 28 + * 29 + * This version assumes that it is safe to read at most 8 bytes past the end of 30 + * @end (we still return an error if the varint extends past @end). 31 + */ 32 + int bch2_varint_decode_fast(const u8 *in, const u8 *end, u64 *out) 92 33 { 93 34 u64 v = get_unaligned_le64(in); 94 35 unsigned bytes = ffz(v & 255) + 1;
+3
fs/bcachefs/varint.h
··· 5 5 int bch2_varint_encode(u8 *, u64); 6 6 int bch2_varint_decode(const u8 *, const u8 *, u64 *); 7 7 8 + int bch2_varint_encode_fast(u8 *, u64); 9 + int bch2_varint_decode_fast(const u8 *, const u8 *, u64 *); 10 + 8 11 #endif /* _BCACHEFS_VARINT_H */