Skip to content

Commit 19c431d

Browse files
authored
Merge pull request trusteddomainproject#431 from thegushi/fix/issue-411-signreq-stack-overflow
opendkim: heap-allocate keydata/tmpdata in dkimf_add_signrequest() (fixes trusteddomainproject#411)
2 parents f6a7def + a0d64e7 commit 19c431d

2 files changed

Lines changed: 44 additions & 8 deletions

File tree

CHANGES-202605.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ This document summarizes the changes merged into the `develop` branch during the
7373
- **Segfault with empty `RequiredHeaders`**: assert in selecthdrs when option was set but produced no headers. (#313, issue #174)
7474
- **`MultipleSignatures` orphaned signreq entries**: Sign request list tail pointer was not maintained, causing use-after-free or missed entries with multiple signatures. (#274)
7575
- **`DKIMF_STATUS_KEYFAIL` undefined**: Missing define caused incorrect handling of key failure status. (#329)
76+
- **Stack overflow risk in `dkimf_add_signrequest()` on macOS ARM64**: The function stack-allocated two `MAXBUFRSZ + 1` (65,537-byte) locals, `keydata` and `tmpdata`, totaling over 128 KB. This is harmless on Linux, where pthreads default to an 8 MB stack, but macOS ARM64 libmilter callback threads default to 512 KB; combined with libmilter's own frames and the rest of the `mlfi_eoh()``dkimf_apply_signtable()` call chain, an active KeyTable could overrun the stack (`SIGBUS`, `___chkstk_darwin` in a crash trace) during ordinary sign-mode operation. Both buffers are now heap-allocated with `malloc()`/`free()` on every return path. No behavior change. (issue #411)
7677

7778
---
7879

opendkim/opendkim.c

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5055,7 +5055,7 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname,
50555055
size_t keydatasz = 0;
50565056
struct signreq *new;
50575057
struct dkimf_db_data dbd[4];
5058-
char keydata[MAXBUFRSZ + 1];
5058+
char *keydata = NULL;
50595059
char domain[DKIM_MAXHOSTNAMELEN + 1];
50605060
char selector[BUFRSZ + 1];
50615061
char signalgstr[BUFRSZ + 1];
@@ -5082,9 +5082,19 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname,
50825082

50835083
assert(keyname != NULL);
50845084

5085+
/*
5086+
** keydata is heap-allocated (rather than a MAXBUFRSZ+1
5087+
** stack array) because this function runs on a libmilter
5088+
** callback thread; on macOS ARM64 those default to a
5089+
** 512 KB stack, which two 64 KB locals can overrun.
5090+
*/
5091+
keydata = malloc(MAXBUFRSZ + 1);
5092+
if (keydata == NULL)
5093+
return -1;
5094+
50855095
memset(domain, '\0', sizeof domain);
50865096
memset(selector, '\0', sizeof selector);
5087-
memset(keydata, '\0', sizeof keydata);
5097+
memset(keydata, '\0', MAXBUFRSZ + 1);
50885098
memset(signalgstr, '\0', sizeof signalgstr);
50895099

50905100
dbd[0].dbdata_buffer = domain;
@@ -5094,7 +5104,7 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname,
50945104
dbd[1].dbdata_buflen = sizeof selector - 1;
50955105
dbd[1].dbdata_flags = DKIMF_DB_DATA_OPTIONAL;
50965106
dbd[2].dbdata_buffer = keydata;
5097-
dbd[2].dbdata_buflen = sizeof keydata - 1;
5107+
dbd[2].dbdata_buflen = MAXBUFRSZ;
50985108
dbd[2].dbdata_flags = DKIMF_DB_DATA_OPTIONAL;
50995109
dbd[3].dbdata_buffer = signalgstr;
51005110
dbd[3].dbdata_buflen = sizeof signalgstr - 1;
@@ -5122,11 +5132,15 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname,
51225132
}
51235133
}
51245134

5135+
free(keydata);
51255136
return -1;
51265137
}
51275138

51285139
if (!found)
5140+
{
5141+
free(keydata);
51295142
return 1;
5143+
}
51305144

51315145
if (dbd[0].dbdata_buflen == 0 ||
51325146
dbd[0].dbdata_buflen == (size_t) -1 ||
@@ -5145,6 +5159,7 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname,
51455159
"key");
51465160
}
51475161

5162+
free(keydata);
51485163
return 2;
51495164
}
51505165

@@ -5158,27 +5173,37 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname,
51585173
keyname);
51595174
}
51605175

5176+
free(keydata);
51615177
return 3;
51625178
}
51635179

51645180
if (keydata[0] == '/')
51655181
{
51665182
char *d;
5167-
char tmpdata[MAXBUFRSZ + 1];
5183+
char *tmpdata;
51685184

5169-
memset(tmpdata, '\0', sizeof tmpdata);
5185+
tmpdata = malloc(MAXBUFRSZ + 1);
5186+
if (tmpdata == NULL)
5187+
{
5188+
free(keydata);
5189+
return -1;
5190+
}
5191+
5192+
memset(tmpdata, '\0', MAXBUFRSZ + 1);
51705193

51715194
if (domain[0] == '%' && domain[1] == '\0')
51725195
d = dfc->mctx_domain;
51735196
else
51745197
d = domain;
51755198

5176-
dkimf_reptoken(tmpdata, sizeof tmpdata, keydata, d);
5199+
dkimf_reptoken(tmpdata, MAXBUFRSZ + 1, keydata, d);
51775200

5178-
memcpy(keydata, tmpdata, sizeof keydata);
5201+
memcpy(keydata, tmpdata, MAXBUFRSZ + 1);
5202+
5203+
free(tmpdata);
51795204
}
51805205

5181-
keydatasz = sizeof keydata - 1;
5206+
keydatasz = MAXBUFRSZ;
51825207
insecure = FALSE;
51835208
if (!dkimf_loadkey(dbd[2].dbdata_buffer, &keydatasz,
51845209
&insecure, err, sizeof err))
@@ -5189,6 +5214,7 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname,
51895214
dbd[2].dbdata_buffer, err);
51905215
}
51915216

5217+
free(keydata);
51925218
return 2;
51935219
}
51945220

@@ -5269,7 +5295,10 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname,
52695295
}
52705296

52715297
if (curconf->conf_safekeys)
5298+
{
5299+
free(keydata);
52725300
return 2;
5301+
}
52735302
}
52745303

52755304
if (dbd[3].dbdata_buflen > 0 && signalgstr[0] != '\0')
@@ -5286,14 +5315,18 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname,
52865315
keyname, signalgstr);
52875316
}
52885317

5318+
free(keydata);
52895319
return 2;
52905320
}
52915321
}
52925322
}
52935323

52945324
new = malloc(sizeof *new);
52955325
if (new == NULL)
5326+
{
5327+
free(keydata);
52965328
return -1;
5329+
}
52975330

52985331
new->srq_next = NULL;
52995332
new->srq_dkim = NULL;
@@ -5322,10 +5355,12 @@ dkimf_add_signrequest(struct msgctx *dfc, DKIMF_DB keytable, char *keyname,
53225355
TRYFREE(new->srq_domain);
53235356
TRYFREE(new->srq_selector);
53245357
free(new);
5358+
free(keydata);
53255359
return -1;
53265360
}
53275361
memset(new->srq_keydata, '\0', keydatasz + 1);
53285362
memcpy(new->srq_keydata, dbd[2].dbdata_buffer, keydatasz);
5363+
free(keydata);
53295364
}
53305365

53315366
if (dfc->mctx_srtail != NULL)

0 commit comments

Comments
 (0)