diff options
author | Joe Slater <joe.slater@windriver.com> | 2021-09-07 12:00:02 -0700 |
---|---|---|
committer | Armin Kuster <akuster808@gmail.com> | 2021-09-07 12:53:53 -0700 |
commit | cea26b8482b83eda18370406f8d59cf63436b5ba (patch) | |
tree | 9e431bd3399e44da7a07f1abddab8b31f058be54 | |
parent | 32f1d758a14bba35d67a75778ae747f1ff5c5482 (diff) | |
download | meta-openembedded-cea26b8482b83eda18370406f8d59cf63436b5ba.tar.gz |
redis: fix CVE-2021-32761
Backport from version 6.2.5.
Signed-off-by: Joe Slater <joe.slater@windriver.com>
Signed-off-by: Armin Kuster <akuster808@gmail.com>
-rw-r--r-- | meta-oe/recipes-extended/redis/redis/CVE-2021-32761.patch | 257 | ||||
-rw-r--r-- | meta-oe/recipes-extended/redis/redis_6.2.2.bb | 1 |
2 files changed, 258 insertions, 0 deletions
diff --git a/meta-oe/recipes-extended/redis/redis/CVE-2021-32761.patch b/meta-oe/recipes-extended/redis/redis/CVE-2021-32761.patch new file mode 100644 index 0000000000..14992b789a --- /dev/null +++ b/meta-oe/recipes-extended/redis/redis/CVE-2021-32761.patch | |||
@@ -0,0 +1,257 @@ | |||
1 | From 835d15b5360e277e6f95529c4d8685946a977ddd Mon Sep 17 00:00:00 2001 | ||
2 | From: Huang Zhw <huang_zhw@126.com> | ||
3 | Date: Wed, 21 Jul 2021 21:25:19 +0800 | ||
4 | Subject: [PATCH 1/1] On 32 bit platform, the bit position of | ||
5 | GETBIT/SETBIT/BITFIELD/BITCOUNT,BITPOS may overflow (see CVE-2021-32761) | ||
6 | (#9191) | ||
7 | |||
8 | GETBIT, SETBIT may access wrong address because of wrap. | ||
9 | BITCOUNT and BITPOS may return wrapped results. | ||
10 | BITFIELD may access the wrong address but also allocate insufficient memory and segfault (see CVE-2021-32761). | ||
11 | |||
12 | This commit uses `uint64_t` or `long long` instead of `size_t`. | ||
13 | related https://github.com/redis/redis/pull/8096 | ||
14 | |||
15 | At 32bit platform: | ||
16 | > setbit bit 4294967295 1 | ||
17 | (integer) 0 | ||
18 | > config set proto-max-bulk-len 536870913 | ||
19 | OK | ||
20 | > append bit "\xFF" | ||
21 | (integer) 536870913 | ||
22 | > getbit bit 4294967296 | ||
23 | (integer) 0 | ||
24 | |||
25 | When the bit index is larger than 4294967295, size_t can't hold bit index. In the past, `proto-max-bulk-len` is limit to 536870912, so there is no problem. | ||
26 | |||
27 | After this commit, bit position is stored in `uint64_t` or `long long`. So when `proto-max-bulk-len > 536870912`, 32bit platforms can still be correct. | ||
28 | |||
29 | For 64bit platform, this problem still exists. The major reason is bit pos 8 times of byte pos. When proto-max-bulk-len is very larger, bit pos may overflow. | ||
30 | But at 64bit platform, we don't have so long string. So this bug may never happen. | ||
31 | |||
32 | Additionally this commit add a test cost `512MB` memory which is tag as `large-memory`. Make freebsd ci and valgrind ci ignore this test. | ||
33 | |||
34 | (cherry picked from commit 71d452876ebf8456afaadd6b3c27988abadd1148)d | ||
35 | --- | ||
36 | |||
37 | CVE: CVE-2021-32761 | ||
38 | |||
39 | Upstream-Status: Backport [835d15b5360e277e6f95529c4d8685946a977ddd] | ||
40 | https://github.com/redis/redis.git | ||
41 | |||
42 | Signed-off-by: Joe Slater <joe.slater@windriver.com> | ||
43 | |||
44 | --- | ||
45 | .github/workflows/daily.yml | 6 +++--- | ||
46 | src/bitops.c | 32 ++++++++++++++++---------------- | ||
47 | src/server.h | 2 +- | ||
48 | tests/unit/bitops.tcl | 28 ++++++++++++++++++++++++++++ | ||
49 | 4 files changed, 48 insertions(+), 20 deletions(-) | ||
50 | |||
51 | diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml | ||
52 | index 9e4630e29..432971a9d 100644 | ||
53 | --- a/.github/workflows/daily.yml | ||
54 | +++ b/.github/workflows/daily.yml | ||
55 | @@ -151,7 +151,7 @@ jobs: | ||
56 | run: | | ||
57 | sudo apt-get update | ||
58 | sudo apt-get install tcl8.6 valgrind -y | ||
59 | - ./runtest --valgrind --verbose --clients 1 --dump-logs | ||
60 | + ./runtest --valgrind --verbose --clients 1 --tags -large-memory --dump-logs | ||
61 | - name: module api test | ||
62 | run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1 | ||
63 | - name: unittest | ||
64 | @@ -171,7 +171,7 @@ jobs: | ||
65 | run: | | ||
66 | sudo apt-get update | ||
67 | sudo apt-get install tcl8.6 valgrind -y | ||
68 | - ./runtest --valgrind --verbose --clients 1 --dump-logs | ||
69 | + ./runtest --valgrind --verbose --clients 1 --tags -large-memory --dump-logs | ||
70 | - name: module api test | ||
71 | run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1 | ||
72 | |||
73 | @@ -260,7 +260,7 @@ jobs: | ||
74 | prepare: pkg install -y bash gmake lang/tcl86 | ||
75 | run: > | ||
76 | gmake && | ||
77 | - ./runtest --accurate --verbose --no-latency --dump-logs && | ||
78 | + ./runtest --accurate --verbose --no-latency --tags -large-memory --dump-logs && | ||
79 | MAKE=gmake ./runtest-moduleapi --verbose && | ||
80 | ./runtest-sentinel && | ||
81 | ./runtest-cluster | ||
82 | diff --git a/src/bitops.c b/src/bitops.c | ||
83 | index afd79ad88..f1c563a41 100644 | ||
84 | --- a/src/bitops.c | ||
85 | +++ b/src/bitops.c | ||
86 | @@ -37,8 +37,8 @@ | ||
87 | /* Count number of bits set in the binary array pointed by 's' and long | ||
88 | * 'count' bytes. The implementation of this function is required to | ||
89 | * work with an input string length up to 512 MB or more (server.proto_max_bulk_len) */ | ||
90 | -size_t redisPopcount(void *s, long count) { | ||
91 | - size_t bits = 0; | ||
92 | +long long redisPopcount(void *s, long count) { | ||
93 | + long long bits = 0; | ||
94 | unsigned char *p = s; | ||
95 | uint32_t *p4; | ||
96 | static const unsigned char bitsinbyte[256] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8}; | ||
97 | @@ -98,11 +98,11 @@ size_t redisPopcount(void *s, long count) { | ||
98 | * no zero bit is found, it returns count*8 assuming the string is zero | ||
99 | * padded on the right. However if 'bit' is 1 it is possible that there is | ||
100 | * not a single set bit in the bitmap. In this special case -1 is returned. */ | ||
101 | -long redisBitpos(void *s, unsigned long count, int bit) { | ||
102 | +long long redisBitpos(void *s, unsigned long count, int bit) { | ||
103 | unsigned long *l; | ||
104 | unsigned char *c; | ||
105 | unsigned long skipval, word = 0, one; | ||
106 | - long pos = 0; /* Position of bit, to return to the caller. */ | ||
107 | + long long pos = 0; /* Position of bit, to return to the caller. */ | ||
108 | unsigned long j; | ||
109 | int found; | ||
110 | |||
111 | @@ -410,7 +410,7 @@ void printBits(unsigned char *p, unsigned long count) { | ||
112 | * If the 'hash' argument is true, and 'bits is positive, then the command | ||
113 | * will also parse bit offsets prefixed by "#". In such a case the offset | ||
114 | * is multiplied by 'bits'. This is useful for the BITFIELD command. */ | ||
115 | -int getBitOffsetFromArgument(client *c, robj *o, size_t *offset, int hash, int bits) { | ||
116 | +int getBitOffsetFromArgument(client *c, robj *o, uint64_t *offset, int hash, int bits) { | ||
117 | long long loffset; | ||
118 | char *err = "bit offset is not an integer or out of range"; | ||
119 | char *p = o->ptr; | ||
120 | @@ -435,7 +435,7 @@ int getBitOffsetFromArgument(client *c, robj *o, size_t *offset, int hash, int b | ||
121 | return C_ERR; | ||
122 | } | ||
123 | |||
124 | - *offset = (size_t)loffset; | ||
125 | + *offset = loffset; | ||
126 | return C_OK; | ||
127 | } | ||
128 | |||
129 | @@ -477,7 +477,7 @@ int getBitfieldTypeFromArgument(client *c, robj *o, int *sign, int *bits) { | ||
130 | * so that the 'maxbit' bit can be addressed. The object is finally | ||
131 | * returned. Otherwise if the key holds a wrong type NULL is returned and | ||
132 | * an error is sent to the client. */ | ||
133 | -robj *lookupStringForBitCommand(client *c, size_t maxbit) { | ||
134 | +robj *lookupStringForBitCommand(client *c, uint64_t maxbit) { | ||
135 | size_t byte = maxbit >> 3; | ||
136 | robj *o = lookupKeyWrite(c->db,c->argv[1]); | ||
137 | if (checkType(c,o,OBJ_STRING)) return NULL; | ||
138 | @@ -527,7 +527,7 @@ unsigned char *getObjectReadOnlyString(robj *o, long *len, char *llbuf) { | ||
139 | void setbitCommand(client *c) { | ||
140 | robj *o; | ||
141 | char *err = "bit is not an integer or out of range"; | ||
142 | - size_t bitoffset; | ||
143 | + uint64_t bitoffset; | ||
144 | ssize_t byte, bit; | ||
145 | int byteval, bitval; | ||
146 | long on; | ||
147 | @@ -566,7 +566,7 @@ void setbitCommand(client *c) { | ||
148 | void getbitCommand(client *c) { | ||
149 | robj *o; | ||
150 | char llbuf[32]; | ||
151 | - size_t bitoffset; | ||
152 | + uint64_t bitoffset; | ||
153 | size_t byte, bit; | ||
154 | size_t bitval = 0; | ||
155 | |||
156 | @@ -888,7 +888,7 @@ void bitposCommand(client *c) { | ||
157 | addReplyLongLong(c, -1); | ||
158 | } else { | ||
159 | long bytes = end-start+1; | ||
160 | - long pos = redisBitpos(p+start,bytes,bit); | ||
161 | + long long pos = redisBitpos(p+start,bytes,bit); | ||
162 | |||
163 | /* If we are looking for clear bits, and the user specified an exact | ||
164 | * range with start-end, we can't consider the right of the range as | ||
165 | @@ -897,11 +897,11 @@ void bitposCommand(client *c) { | ||
166 | * So if redisBitpos() returns the first bit outside the range, | ||
167 | * we return -1 to the caller, to mean, in the specified range there | ||
168 | * is not a single "0" bit. */ | ||
169 | - if (end_given && bit == 0 && pos == bytes*8) { | ||
170 | + if (end_given && bit == 0 && pos == (long long)bytes<<3) { | ||
171 | addReplyLongLong(c,-1); | ||
172 | return; | ||
173 | } | ||
174 | - if (pos != -1) pos += start*8; /* Adjust for the bytes we skipped. */ | ||
175 | + if (pos != -1) pos += (long long)start<<3; /* Adjust for the bytes we skipped. */ | ||
176 | addReplyLongLong(c,pos); | ||
177 | } | ||
178 | } | ||
179 | @@ -933,12 +933,12 @@ struct bitfieldOp { | ||
180 | * GET subcommand is allowed, other subcommands will return an error. */ | ||
181 | void bitfieldGeneric(client *c, int flags) { | ||
182 | robj *o; | ||
183 | - size_t bitoffset; | ||
184 | + uint64_t bitoffset; | ||
185 | int j, numops = 0, changes = 0; | ||
186 | struct bitfieldOp *ops = NULL; /* Array of ops to execute at end. */ | ||
187 | int owtype = BFOVERFLOW_WRAP; /* Overflow type. */ | ||
188 | int readonly = 1; | ||
189 | - size_t highest_write_offset = 0; | ||
190 | + uint64_t highest_write_offset = 0; | ||
191 | |||
192 | for (j = 2; j < c->argc; j++) { | ||
193 | int remargs = c->argc-j-1; /* Remaining args other than current. */ | ||
194 | @@ -1128,9 +1128,9 @@ void bitfieldGeneric(client *c, int flags) { | ||
195 | * object boundaries. */ | ||
196 | memset(buf,0,9); | ||
197 | int i; | ||
198 | - size_t byte = thisop->offset >> 3; | ||
199 | + uint64_t byte = thisop->offset >> 3; | ||
200 | for (i = 0; i < 9; i++) { | ||
201 | - if (src == NULL || i+byte >= (size_t)strlen) break; | ||
202 | + if (src == NULL || i+byte >= (uint64_t)strlen) break; | ||
203 | buf[i] = src[i+byte]; | ||
204 | } | ||
205 | |||
206 | diff --git a/src/server.h b/src/server.h | ||
207 | index 67541fe60..caf9df31c 100644 | ||
208 | --- a/src/server.h | ||
209 | +++ b/src/server.h | ||
210 | @@ -1795,7 +1795,7 @@ void getRandomHexChars(char *p, size_t len); | ||
211 | void getRandomBytes(unsigned char *p, size_t len); | ||
212 | uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l); | ||
213 | void exitFromChild(int retcode); | ||
214 | -size_t redisPopcount(void *s, long count); | ||
215 | +long long redisPopcount(void *s, long count); | ||
216 | int redisSetProcTitle(char *title); | ||
217 | int validateProcTitleTemplate(const char *template); | ||
218 | int redisCommunicateSystemd(const char *sd_notify_msg); | ||
219 | diff --git a/tests/unit/bitops.tcl b/tests/unit/bitops.tcl | ||
220 | index 926f38295..534832974 100644 | ||
221 | --- a/tests/unit/bitops.tcl | ||
222 | +++ b/tests/unit/bitops.tcl | ||
223 | @@ -349,3 +349,31 @@ start_server {tags {"bitops"}} { | ||
224 | } | ||
225 | } | ||
226 | } | ||
227 | + | ||
228 | +start_server {tags {"bitops large-memory"}} { | ||
229 | + test "BIT pos larger than UINT_MAX" { | ||
230 | + set bytes [expr (1 << 29) + 1] | ||
231 | + set bitpos [expr (1 << 32)] | ||
232 | + set oldval [lindex [r config get proto-max-bulk-len] 1] | ||
233 | + r config set proto-max-bulk-len $bytes | ||
234 | + r setbit mykey $bitpos 1 | ||
235 | + assert_equal $bytes [r strlen mykey] | ||
236 | + assert_equal 1 [r getbit mykey $bitpos] | ||
237 | + assert_equal [list 128 128 -1] [r bitfield mykey get u8 $bitpos set u8 $bitpos 255 get i8 $bitpos] | ||
238 | + assert_equal $bitpos [r bitpos mykey 1] | ||
239 | + assert_equal $bitpos [r bitpos mykey 1 [expr $bytes - 1]] | ||
240 | + if {$::accurate} { | ||
241 | + # set all bits to 1 | ||
242 | + set mega [expr (1 << 23)] | ||
243 | + set part [string repeat "\xFF" $mega] | ||
244 | + for {set i 0} {$i < 64} {incr i} { | ||
245 | + r setrange mykey [expr $i * $mega] $part | ||
246 | + } | ||
247 | + r setrange mykey [expr $bytes - 1] "\xFF" | ||
248 | + assert_equal [expr $bitpos + 8] [r bitcount mykey] | ||
249 | + assert_equal -1 [r bitpos mykey 0 0 [expr $bytes - 1]] | ||
250 | + } | ||
251 | + r config set proto-max-bulk-len $oldval | ||
252 | + r del mykey | ||
253 | + } {1} | ||
254 | +} | ||
255 | -- | ||
256 | 2.24.1 | ||
257 | |||
diff --git a/meta-oe/recipes-extended/redis/redis_6.2.2.bb b/meta-oe/recipes-extended/redis/redis_6.2.2.bb index a9e6eaffaa..ad675e9e04 100644 --- a/meta-oe/recipes-extended/redis/redis_6.2.2.bb +++ b/meta-oe/recipes-extended/redis/redis_6.2.2.bb | |||
@@ -19,6 +19,7 @@ SRC_URI = "http://download.redis.io/releases/${BP}.tar.gz \ | |||
19 | file://fix-CVE-2021-29477.patch \ | 19 | file://fix-CVE-2021-29477.patch \ |
20 | file://fix-CVE-2021-29478.patch \ | 20 | file://fix-CVE-2021-29478.patch \ |
21 | file://fix-CVE-2021-32625.patch \ | 21 | file://fix-CVE-2021-32625.patch \ |
22 | file://CVE-2021-32761.patch \ | ||
22 | " | 23 | " |
23 | SRC_URI[sha256sum] = "7a260bb74860f1b88c3d5942bf8ba60ca59f121c6dce42d3017bed6add0b9535" | 24 | SRC_URI[sha256sum] = "7a260bb74860f1b88c3d5942bf8ba60ca59f121c6dce42d3017bed6add0b9535" |
24 | 25 | ||