diff options
author | Andy Wingo <wingo@pobox.com> | 2017-02-08 15:05:03 +0100 |
---|---|---|
committer | Andy Wingo <wingo@pobox.com> | 2017-02-08 15:09:14 +0100 |
commit | 09a69dd712536350b4b8feec8cdec3dc49cb71d5 (patch) | |
tree | ecf2fd427208fee72efbc0e47ca2da863eee3ba2 /libguile/ports-internal.h | |
parent | 8a4774dec8368def01af4126e77797468b0ce6de (diff) | |
download | guile-09a69dd712536350b4b8feec8cdec3dc49cb71d5.tar.gz |
Prevent TOCTTOU bugs in C ports
* libguile/ports-internal.h (scm_port_buffer_can_take):
(scm_port_buffer_can_put): Add cur/end output arguments so that when a
caller asks the buffer room, it can be relative to a fixed point in
the buffer and not whatever point it's at when we go to fill it.
(scm_port_buffer_did_take, scm_port_buffer_did_put): Similarly,
require that the caller knows where they took/put data in the buffer.
Prevents overflow.
(scm_port_buffer_take_pointer, scm_port_buffer_put_pointer): Likewise,
require that the caller has already checked and knows a position in
the buffer and therefore how much data is available.
(scm_port_buffer_take, scm_port_buffer_put, scm_port_buffer_putback):
Adapt.
* libguile/ports.h (scm_fill_input): Add cur/avail output arguments.
* libguile/filesys.c:
* libguile/poll.c:
* libguile/ports.c:
* libguile/r6rs-ports.c:
* libguile/read.c:
* libguile/rw.c: Adapt all callers. Gnarly work!
Diffstat (limited to 'libguile/ports-internal.h')
-rw-r--r-- | libguile/ports-internal.h | 55 |
1 files changed, 30 insertions, 25 deletions
diff --git a/libguile/ports-internal.h b/libguile/ports-internal.h index 4203a5c51..be7ba60f5 100644 --- a/libguile/ports-internal.h +++ b/libguile/ports-internal.h @@ -212,22 +212,26 @@ scm_port_buffer_reset_end (SCM buf) } static inline size_t -scm_port_buffer_can_take (SCM buf) +scm_port_buffer_can_take (SCM buf, size_t *cur_out) { size_t cur, end; cur = scm_to_size_t (scm_port_buffer_cur (buf)); end = scm_to_size_t (scm_port_buffer_end (buf)); - if (cur > end || end > scm_port_buffer_size (buf)) + if (end > scm_port_buffer_size (buf)) scm_misc_error (NULL, "invalid port buffer ~a", scm_list_1 (buf)); - return end - cur; + /* If something races and we end up with end < cur, signal the caller + to do a fill_input and centralize there. */ + *cur_out = cur; + return end < cur ? 0 : end - cur; } static inline size_t -scm_port_buffer_can_put (SCM buf) +scm_port_buffer_can_put (SCM buf, size_t *end_out) { size_t end = scm_to_size_t (scm_port_buffer_end (buf)); if (end > scm_port_buffer_size (buf)) scm_misc_error (NULL, "invalid port buffer ~a", scm_list_1 (buf)); + *end_out = end; return scm_port_buffer_size (buf) - end; } @@ -241,58 +245,59 @@ scm_port_buffer_can_putback (SCM buf) } static inline void -scm_port_buffer_did_take (SCM buf, size_t count) +scm_port_buffer_did_take (SCM buf, size_t prev_cur, size_t count) { - scm_port_buffer_set_cur - (buf, SCM_I_MAKINUM (SCM_I_INUM (scm_port_buffer_cur (buf)) + count)); + scm_port_buffer_set_cur (buf, SCM_I_MAKINUM (prev_cur + count)); } static inline void -scm_port_buffer_did_put (SCM buf, size_t count) +scm_port_buffer_did_put (SCM buf, size_t prev_end, size_t count) { - scm_port_buffer_set_end - (buf, SCM_I_MAKINUM (SCM_I_INUM (scm_port_buffer_end (buf)) + count)); + scm_port_buffer_set_end (buf, SCM_I_MAKINUM (prev_end + count)); } static inline const scm_t_uint8 * -scm_port_buffer_take_pointer (SCM buf) +scm_port_buffer_take_pointer (SCM buf, size_t cur) { signed char *ret = SCM_BYTEVECTOR_CONTENTS (scm_port_buffer_bytevector (buf)); - return ((scm_t_uint8 *) ret) + scm_to_size_t (scm_port_buffer_cur (buf)); + return ((scm_t_uint8 *) ret) + cur; } static inline scm_t_uint8 * -scm_port_buffer_put_pointer (SCM buf) +scm_port_buffer_put_pointer (SCM buf, size_t end) { signed char *ret = SCM_BYTEVECTOR_CONTENTS (scm_port_buffer_bytevector (buf)); - return ((scm_t_uint8 *) ret) + scm_to_size_t (scm_port_buffer_end (buf)); + return ((scm_t_uint8 *) ret) + end; } static inline size_t -scm_port_buffer_take (SCM buf, scm_t_uint8 *dst, size_t count) +scm_port_buffer_take (SCM buf, scm_t_uint8 *dst, size_t count, + size_t cur, size_t avail) { - count = min (count, scm_port_buffer_can_take (buf)); + if (avail < count) + count = avail; if (dst) - memcpy (dst, scm_port_buffer_take_pointer (buf), count); - scm_port_buffer_did_take (buf, count); + memcpy (dst, scm_port_buffer_take_pointer (buf, cur), count); + scm_port_buffer_did_take (buf, cur, count); return count; } static inline size_t -scm_port_buffer_put (SCM buf, const scm_t_uint8 *src, size_t count) +scm_port_buffer_put (SCM buf, const scm_t_uint8 *src, size_t count, + size_t end, size_t avail) { - count = min (count, scm_port_buffer_can_put (buf)); + if (avail < count) + count = avail; if (src) - memcpy (scm_port_buffer_put_pointer (buf), src, count); - scm_port_buffer_did_put (buf, count); + memcpy (scm_port_buffer_put_pointer (buf, end), src, count); + scm_port_buffer_did_put (buf, end, count); return count; } static inline void -scm_port_buffer_putback (SCM buf, const scm_t_uint8 *src, size_t count) +scm_port_buffer_putback (SCM buf, const scm_t_uint8 *src, size_t count, + size_t cur) { - size_t cur = scm_to_size_t (scm_port_buffer_cur (buf)); - assert (count <= cur); /* Sometimes used to move around data within a buffer, so we must use |