Skip to content

Commit 6c9d3dd

Browse files
authored
Fix pcompress/zlib implementation (#2625)
* pcompress/zlib: Check for correct return values. deflate and inflate with Z_FINISH return Z_STREAM_END on success. All other cases imply that an error occurred or that not enough output space was available. These cases should be treated as errors because: - deflateBound specifies max amount of output bytes to expect - inflate takes length from message into account Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org> * pcompress/zlib: Use correct data types. On 64 bit systems size_t is larger than uint32_t. This means that performing a memcpy() with sizeof(uint32_t) truncates the value. Also avoid signed data types when unsigned types are better suited. Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org> * pcompress/zlib: Correctly terminate string. Right now each successful operation leads to out of boundary heap access by not dereferencing the double pointer outstring. This is supposed to terminate the string with a '\0', not setting a char pointer to NULL. Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org> * pcompress/zlib: Validate input length. Check that input length is not UINT32_MAX to avoid integer overflow. If such an overflow occurs, a malicious peer could trigger an out of boundary heap access when terminating the string with a nul byte. Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
1 parent ac0654a commit 6c9d3dd

File tree

1 file changed

+17
-10
lines changed

1 file changed

+17
-10
lines changed

src/mca/pcompress/zlib/compress_zlib.c

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,17 @@ static bool zlib_compress(const uint8_t *inbytes, size_t inlen, uint8_t **outbyt
6060
z_stream strm;
6161
size_t len, len2;
6262
uint8_t *tmp, *ptr;
63+
uint32_t len3;
6364
int rc;
6465

6566
/* set default output */
6667
*outbytes = NULL;
6768
*outlen = 0;
6869

69-
if (inlen < pmix_compress_base.compress_limit) {
70+
if (inlen < pmix_compress_base.compress_limit || inlen >= UINT32_MAX) {
7071
return false;
7172
}
73+
len3 = inlen;
7274

7375
/* setup the stream */
7476
memset(&strm, 0, sizeof(strm));
@@ -99,7 +101,7 @@ static bool zlib_compress(const uint8_t *inbytes, size_t inlen, uint8_t **outbyt
99101

100102
rc = deflate(&strm, Z_FINISH);
101103
(void) deflateEnd(&strm);
102-
if (Z_OK != rc && Z_STREAM_END != rc) {
104+
if (Z_STREAM_END != rc) {
103105
free(tmp);
104106
return false;
105107
}
@@ -117,7 +119,7 @@ static bool zlib_compress(const uint8_t *inbytes, size_t inlen, uint8_t **outbyt
117119
*outlen = len2;
118120

119121
/* fold the uncompressed length into the buffer */
120-
memcpy(ptr, &inlen, sizeof(uint32_t));
122+
memcpy(ptr, &len3, sizeof(uint32_t));
121123
ptr += sizeof(uint32_t);
122124
/* bring over the compressed data */
123125
memcpy(ptr, tmp, len2 - sizeof(uint32_t));
@@ -167,7 +169,7 @@ static bool doit(uint8_t **outbytes, size_t len2, const uint8_t *inbytes, size_t
167169

168170
rc = inflate(&strm, Z_FINISH);
169171
inflateEnd(&strm);
170-
if (Z_OK == rc) {
172+
if (Z_STREAM_END == rc) {
171173
*outbytes = dest;
172174
return true;
173175
}
@@ -176,7 +178,7 @@ static bool doit(uint8_t **outbytes, size_t len2, const uint8_t *inbytes, size_t
176178
}
177179
static bool zlib_decompress(uint8_t **outbytes, size_t *outlen, const uint8_t *inbytes, size_t inlen)
178180
{
179-
int32_t len2;
181+
uint32_t len2;
180182
bool rc;
181183
uint8_t *input;
182184

@@ -187,7 +189,7 @@ static bool zlib_decompress(uint8_t **outbytes, size_t *outlen, const uint8_t *i
187189
memcpy(&len2, inbytes, sizeof(uint32_t));
188190

189191
pmix_output_verbose(2, pmix_pcompress_base_framework.framework_output,
190-
"DECOMPRESSING INPUT OF LEN %" PRIsize_t " OUTPUT %d", inlen, len2);
192+
"DECOMPRESSING INPUT OF LEN %" PRIsize_t " OUTPUT %u", inlen, len2);
191193

192194
input = (uint8_t *) (inbytes + sizeof(uint32_t)); // step over the size
193195
rc = doit(outbytes, len2, input, inlen);
@@ -200,22 +202,27 @@ static bool zlib_decompress(uint8_t **outbytes, size_t *outlen, const uint8_t *i
200202

201203
static bool decompress_string(char **outstring, uint8_t *inbytes, size_t len)
202204
{
203-
int32_t len2;
205+
uint32_t len2;
204206
bool rc;
205207
uint8_t *input;
206208

207209
/* the first 4 bytes contains the uncompressed size */
208210
memcpy(&len2, inbytes, sizeof(uint32_t));
209-
/* add one to hold the NULL terminator */
211+
if (len2 == UINT32_MAX) {
212+
/* set the default error answer */
213+
*outstring = NULL;
214+
return false;
215+
}
216+
/* add one to hold the NUL terminator */
210217
++len2;
211218

212219
/* decompress the bytes */
213220
input = (uint8_t *) (inbytes + sizeof(uint32_t)); // step over the size
214221
rc = doit((uint8_t **) outstring, len2, input, len);
215222

216223
if (rc) {
217-
/* ensure this is NULL terminated! */
218-
outstring[len2 - 1] = NULL;
224+
/* ensure this is NUL terminated! */
225+
*outstring[len2 - 1] = '\0';
219226
return true;
220227
}
221228

0 commit comments

Comments
 (0)