Skip to content

Commit 965bde2

Browse files
authored
Add basic "security rules" (#4298)
1 parent 6d09496 commit 965bde2

File tree

8 files changed

+844
-0
lines changed

8 files changed

+844
-0
lines changed

stuffer/s2n_stuffer.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#pragma once
1717

1818
#include <limits.h>
19+
#include <stdarg.h>
1920
#include <stdint.h>
2021
#include <stdlib.h>
2122
#include <sys/uio.h>
@@ -165,6 +166,16 @@ int s2n_stuffer_skip_read_until(struct s2n_stuffer *stuffer, const char *target)
165166
int s2n_stuffer_alloc_ro_from_string(struct s2n_stuffer *stuffer, const char *str);
166167
int s2n_stuffer_init_ro_from_string(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t length);
167168

169+
/* Stuffer versions of sprintf methods, except:
170+
* - They write bytes, not strings. They do not write a final '\0'. Unfortunately,
171+
* they do still require enough space for a final '\0'-- we'd have to reimplement
172+
* sprintf to avoid that.
173+
* - vprintf does not consume the vargs. It calls va_copy before using
174+
* the varg argument, so can be called repeatedly with the same vargs.
175+
*/
176+
int s2n_stuffer_printf(struct s2n_stuffer *stuffer, const char *format, ...);
177+
int s2n_stuffer_vprintf(struct s2n_stuffer *stuffer, const char *format, va_list vargs);
178+
168179
/* Read a private key from a PEM encoded stuffer to an ASN1/DER encoded one */
169180
int s2n_stuffer_private_key_from_pem(struct s2n_stuffer *pem, struct s2n_stuffer *asn1, int *type);
170181

stuffer/s2n_stuffer_text.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
#include <string.h>
17+
#include <sys/param.h>
1718

1819
#include "stuffer/s2n_stuffer.h"
1920
#include "utils/s2n_mem.h"
@@ -206,3 +207,73 @@ int s2n_stuffer_init_ro_from_string(struct s2n_stuffer *stuffer, uint8_t *data,
206207

207208
return S2N_SUCCESS;
208209
}
210+
211+
/* If we call va_start or va_copy there MUST be a matching call to va_end,
212+
* so we should use DEFER_CLEANUP with our va_lists.
213+
* Unfortunately, some environments implement va_list in ways that don't
214+
* act as expected when passed by reference. For example, because va_end is
215+
* a macro it may expect va_list to be an array (maybe to call sizeof),
216+
* but passing va_list by reference will cause it to decay to a pointer instead.
217+
* To avoid any surprises, just wrap the va_list in our own struct.
218+
*/
219+
struct s2n_va_list {
220+
va_list va_list;
221+
};
222+
223+
static void s2n_va_list_cleanup(struct s2n_va_list *list)
224+
{
225+
if (list) {
226+
va_end(list->va_list);
227+
}
228+
}
229+
230+
int s2n_stuffer_vprintf(struct s2n_stuffer *stuffer, const char *format, va_list vargs_in)
231+
{
232+
POSIX_PRECONDITION(s2n_stuffer_validate(stuffer));
233+
POSIX_ENSURE_REF(format);
234+
235+
/* vsnprintf consumes the va_list, so copy it first */
236+
DEFER_CLEANUP(struct s2n_va_list vargs_1 = { 0 }, s2n_va_list_cleanup);
237+
va_copy(vargs_1.va_list, vargs_in);
238+
239+
/* The first call to vsnprintf calculates the size of the formatted string.
240+
* str_len does not include the one byte vsnprintf requires for a trailing '\0',
241+
* so we need one more byte.
242+
*/
243+
int str_len = vsnprintf(NULL, 0, format, vargs_1.va_list);
244+
POSIX_ENSURE_GTE(str_len, 0);
245+
int mem_size = str_len + 1;
246+
247+
/* 'tainted' indicates that pointers to the contents of the stuffer exist,
248+
* so resizing / reallocated the stuffer will invalidate those pointers.
249+
* However, we do no resize the stuffer in this method after creating `str`
250+
* and `str` does not live beyond this method, so ignore `str` for the
251+
* purposes of tracking 'tainted'.
252+
*/
253+
bool previously_tainted = stuffer->tainted;
254+
char *str = s2n_stuffer_raw_write(stuffer, mem_size);
255+
stuffer->tainted = previously_tainted;
256+
POSIX_GUARD_PTR(str);
257+
258+
/* vsnprintf again consumes the va_list, so copy it first */
259+
DEFER_CLEANUP(struct s2n_va_list vargs_2 = { 0 }, s2n_va_list_cleanup);
260+
va_copy(vargs_2.va_list, vargs_in);
261+
262+
/* This time, vsnprintf actually writes the formatted string */
263+
int written = vsnprintf(str, mem_size, format, vargs_2.va_list);
264+
POSIX_ENSURE_GTE(written, 0);
265+
266+
/* We don't actually use c-strings, so erase the final '\0' */
267+
POSIX_GUARD(s2n_stuffer_wipe_n(stuffer, 1));
268+
269+
POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer));
270+
return S2N_SUCCESS;
271+
}
272+
273+
int s2n_stuffer_printf(struct s2n_stuffer *stuffer, const char *format, ...)
274+
{
275+
DEFER_CLEANUP(struct s2n_va_list vargs = { 0 }, s2n_va_list_cleanup);
276+
va_start(vargs.va_list, format);
277+
POSIX_GUARD(s2n_stuffer_vprintf(stuffer, format, vargs.va_list));
278+
return S2N_SUCCESS;
279+
}

0 commit comments

Comments
 (0)