Skip to content

Commit fb488f2

Browse files
author
simon
committed
Tighten up a lot of casts from unsigned to int which are read by one
of the GET_32BIT macros and then used as length fields. Missing bounds checks against zero have been added, and also I've introduced a helper function toint() which casts from unsigned to int in such a way as to avoid C undefined behaviour, since I'm not sure I trust compilers any more to do the obviously sensible thing. git-svn-id: svn://svn.tartarus.org/sgt/putty@9918 cda61777-01e9-0310-a592-d414129be87e
1 parent 2661129 commit fb488f2

File tree

11 files changed

+185
-70
lines changed

11 files changed

+185
-70
lines changed

conf.c

+5-4
Original file line numberDiff line numberDiff line change
@@ -522,14 +522,15 @@ int conf_deserialise(Conf *conf, void *vdata, int maxsize)
522522
unsigned char *data = (unsigned char *)vdata;
523523
unsigned char *start = data;
524524
struct conf_entry *entry;
525-
int primary, used;
525+
unsigned primary;
526+
int used;
526527
unsigned char *zero;
527528

528529
while (maxsize >= 4) {
529530
primary = GET_32BIT_MSB_FIRST(data);
530531
data += 4, maxsize -= 4;
531532

532-
if ((unsigned)primary >= N_CONFIG_OPTIONS)
533+
if (primary >= N_CONFIG_OPTIONS)
533534
break;
534535

535536
entry = snew(struct conf_entry);
@@ -541,7 +542,7 @@ int conf_deserialise(Conf *conf, void *vdata, int maxsize)
541542
sfree(entry);
542543
goto done;
543544
}
544-
entry->key.secondary.i = GET_32BIT_MSB_FIRST(data);
545+
entry->key.secondary.i = toint(GET_32BIT_MSB_FIRST(data));
545546
data += 4, maxsize -= 4;
546547
break;
547548
case TYPE_STR:
@@ -564,7 +565,7 @@ int conf_deserialise(Conf *conf, void *vdata, int maxsize)
564565
sfree(entry);
565566
goto done;
566567
}
567-
entry->value.u.intval = GET_32BIT_MSB_FIRST(data);
568+
entry->value.u.intval = toint(GET_32BIT_MSB_FIRST(data));
568569
data += 4, maxsize -= 4;
569570
break;
570571
case TYPE_STR:

import.c

+30-11
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ static int ssh2_read_mpint(void *data, int len, struct mpint_pos *ret)
289289

290290
if (len < 4)
291291
goto error;
292-
bytes = GET_32BIT(d);
292+
bytes = toint(GET_32BIT(d));
293293
if (bytes < 0 || len-4 < bytes)
294294
goto error;
295295

@@ -745,6 +745,10 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key,
745745
struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1;
746746
Bignum bd, bp, bq, bdmp1, bdmq1;
747747

748+
/*
749+
* These blobs were generated from inside PuTTY, so we needn't
750+
* treat them as untrusted.
751+
*/
748752
pos = 4 + GET_32BIT(pubblob);
749753
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e);
750754
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n);
@@ -798,6 +802,10 @@ int openssh_write(const Filename *filename, struct ssh2_userkey *key,
798802
int pos;
799803
struct mpint_pos p, q, g, y, x;
800804

805+
/*
806+
* These blobs were generated from inside PuTTY, so we needn't
807+
* treat them as untrusted.
808+
*/
801809
pos = 4 + GET_32BIT(pubblob);
802810
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p);
803811
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q);
@@ -1216,11 +1224,12 @@ int sshcom_encrypted(const Filename *filename, char **comment)
12161224
pos = 8;
12171225
if (key->keyblob_len < pos+4)
12181226
goto done; /* key is far too short */
1219-
pos += 4 + GET_32BIT(key->keyblob + pos); /* skip key type */
1220-
if (key->keyblob_len < pos+4)
1227+
len = toint(GET_32BIT(key->keyblob + pos));
1228+
if (len < 0 || len > key->keyblob_len - pos - 4)
12211229
goto done; /* key is far too short */
1222-
len = GET_32BIT(key->keyblob + pos); /* find cipher-type length */
1223-
if (key->keyblob_len < pos+4+len)
1230+
pos += 4 + len; /* skip key type */
1231+
len = toint(GET_32BIT(key->keyblob + pos)); /* find cipher-type length */
1232+
if (len < 0 || len > key->keyblob_len - pos - 4)
12241233
goto done; /* cipher type string is incomplete */
12251234
if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4))
12261235
answer = 1;
@@ -1236,8 +1245,7 @@ int sshcom_encrypted(const Filename *filename, char **comment)
12361245

12371246
static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret)
12381247
{
1239-
int bits;
1240-
int bytes;
1248+
unsigned bits, bytes;
12411249
unsigned char *d = (unsigned char *) data;
12421250

12431251
if (len < 4)
@@ -1309,7 +1317,8 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
13091317
*/
13101318
pos = 8;
13111319
if (key->keyblob_len < pos+4 ||
1312-
(len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {
1320+
(len = toint(GET_32BIT(key->keyblob + pos))) < 0 ||
1321+
len > key->keyblob_len - pos - 4) {
13131322
errmsg = "key blob does not contain a key type string";
13141323
goto error;
13151324
}
@@ -1329,7 +1338,8 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
13291338
* Determine the cipher type.
13301339
*/
13311340
if (key->keyblob_len < pos+4 ||
1332-
(len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {
1341+
(len = toint(GET_32BIT(key->keyblob + pos))) < 0 ||
1342+
len > key->keyblob_len - pos - 4) {
13331343
errmsg = "key blob does not contain a cipher type string";
13341344
goto error;
13351345
}
@@ -1347,7 +1357,8 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
13471357
* Get hold of the encrypted part of the key.
13481358
*/
13491359
if (key->keyblob_len < pos+4 ||
1350-
(len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {
1360+
(len = toint(GET_32BIT(key->keyblob + pos))) < 0 ||
1361+
len > key->keyblob_len - pos - 4) {
13511362
errmsg = "key blob does not contain actual key data";
13521363
goto error;
13531364
}
@@ -1411,7 +1422,7 @@ struct ssh2_userkey *sshcom_read(const Filename *filename, char *passphrase,
14111422
/*
14121423
* Strip away the containing string to get to the real meat.
14131424
*/
1414-
len = GET_32BIT(ciphertext);
1425+
len = toint(GET_32BIT(ciphertext));
14151426
if (len < 0 || len > cipherlen-4) {
14161427
errmsg = "containing string was ill-formed";
14171428
goto error;
@@ -1540,6 +1551,10 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
15401551
int pos;
15411552
struct mpint_pos n, e, d, p, q, iqmp;
15421553

1554+
/*
1555+
* These blobs were generated from inside PuTTY, so we needn't
1556+
* treat them as untrusted.
1557+
*/
15431558
pos = 4 + GET_32BIT(pubblob);
15441559
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e);
15451560
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n);
@@ -1565,6 +1580,10 @@ int sshcom_write(const Filename *filename, struct ssh2_userkey *key,
15651580
int pos;
15661581
struct mpint_pos p, q, g, y, x;
15671582

1583+
/*
1584+
* These blobs were generated from inside PuTTY, so we needn't
1585+
* treat them as untrusted.
1586+
*/
15681587
pos = 4 + GET_32BIT(pubblob);
15691588
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p);
15701589
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q);

misc.c

+23
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,29 @@ void burnstr(char *string) /* sfree(str), only clear it first */
208208
}
209209
}
210210

211+
int toint(unsigned u)
212+
{
213+
/*
214+
* Convert an unsigned to an int, without running into the
215+
* undefined behaviour which happens by the strict C standard if
216+
* the value overflows. You'd hope that sensible compilers would
217+
* do the sensible thing in response to a cast, but actually I
218+
* don't trust modern compilers not to do silly things like
219+
* assuming that _obviously_ you wouldn't have caused an overflow
220+
* and so they can elide an 'if (i < 0)' test immediately after
221+
* the cast.
222+
*
223+
* Sensible compilers ought of course to optimise this entire
224+
* function into 'just return the input value'!
225+
*/
226+
if (u <= (unsigned)INT_MAX)
227+
return (int)u;
228+
else if (u >= (unsigned)INT_MIN) /* wrap in cast _to_ unsigned is OK */
229+
return INT_MIN + (int)(u - (unsigned)INT_MIN);
230+
else
231+
return INT_MIN; /* fallback; should never occur on binary machines */
232+
}
233+
211234
/*
212235
* Do an sprintf(), but into a custom-allocated buffer.
213236
*

misc.h

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ char *dupprintf(const char *fmt, ...);
3030
char *dupvprintf(const char *fmt, va_list ap);
3131
void burnstr(char *string);
3232

33+
int toint(unsigned);
34+
3335
char *fgetline(FILE *fp);
3436

3537
void base64_encode_atom(unsigned char *data, int n, char *out);

sftp.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ static int sftp_pkt_getstring(struct sftp_packet *pkt,
150150
*p = NULL;
151151
if (pkt->length - pkt->savedpos < 4)
152152
return 0;
153-
*length = GET_32BIT(pkt->data + pkt->savedpos);
153+
*length = toint(GET_32BIT(pkt->data + pkt->savedpos));
154154
pkt->savedpos += 4;
155155
if ((int)(pkt->length - pkt->savedpos) < *length || *length < 0) {
156156
*length = 0;

0 commit comments

Comments
 (0)