diff --git a/CHANGELOG.md b/CHANGELOG.md index 02191aa6..616475bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ Please notice that this change log contains changes for upcoming releases as wel ## Changes: +#### Change log v.0.7.34 + +**Security**: (`facil.io`, `http`) updated to facil.io 0.7.3, incorporating it's bug fixes and security updates. + #### Change log v.0.7.33 **Fix**: (`iodine`) exception protection would fail and crash if the exception throws wasn't of type `Exception`. I'm not sure how this would happen, but on some Ruby versions it appeared to have occur, maybe where a custom `raise` would be called with a non-exception type. The issue was fixed by testing for the availability of the `message` and `backtrace` functions. Credit to Jan Biedermann (@janbiedermann) for exposing this issue (#76). diff --git a/ext/iodine/fio.c b/ext/iodine/fio.c index b4745b51..59f0a7ef 100644 --- a/ext/iodine/fio.c +++ b/ext/iodine/fio.c @@ -1178,12 +1178,13 @@ static inline void fio_mark_time(void) { /** Calculates the due time for a task, given it's interval */ static struct timespec fio_timer_calc_due(size_t interval) { struct timespec now = fio_last_tick(); - if (interval > 1000) { - now.tv_sec += interval / 1000; - interval -= interval / 1000; + if (interval >= 1000) { + unsigned long long secs = interval / 1000; + now.tv_sec += secs; + interval -= secs * 1000; } now.tv_nsec += (interval * 1000000UL); - if (now.tv_nsec > 1000000000L) { + if (now.tv_nsec >= 1000000000L) { now.tv_nsec -= 1000000000L; now.tv_sec += 1; } @@ -1346,7 +1347,7 @@ Section Start Marker ***************************************************************************** */ volatile uint8_t fio_signal_children_flag = 0; - +volatile fio_lock_i fio_signal_set_flag = 0; /* store old signal handlers to propegate signal handling */ static struct sigaction fio_old_sig_chld; static struct sigaction fio_old_sig_pipe; @@ -1415,7 +1416,7 @@ static void sig_int_handler(int sig) { break; } /* propagate signale handling to previous existing handler (if any) */ - if (old->sa_handler != SIG_IGN && old->sa_handler != SIG_DFL) + if (old && old->sa_handler != SIG_IGN && old->sa_handler != SIG_DFL) old->sa_handler(sig); } @@ -1423,7 +1424,7 @@ static void sig_int_handler(int sig) { static void fio_signal_handler_setup(void) { /* setup signal handling */ struct sigaction act; - if (fio_old_sig_int.sa_handler) + if (fio_trylock(&fio_signal_set_flag)) return; memset(&act, 0, sizeof(act)); @@ -1457,8 +1458,9 @@ static void fio_signal_handler_setup(void) { void fio_signal_handler_reset(void) { struct sigaction old; - if (!fio_old_sig_int.sa_handler) + if (fio_signal_set_flag) return; + fio_unlock(&fio_signal_set_flag); memset(&old, 0, sizeof(old)); sigaction(SIGINT, &fio_old_sig_int, &old); sigaction(SIGTERM, &fio_old_sig_term, &old); @@ -2968,7 +2970,7 @@ ssize_t fio_flush(intptr_t uuid) { goto test_errno; } - if (uuid_data(uuid).packet_count >= 1024 && + if (uuid_data(uuid).packet_count >= FIO_SLOWLORIS_LIMIT && uuid_data(uuid).packet == old_packet && uuid_data(uuid).sent >= old_sent && (uuid_data(uuid).sent - old_sent) < 32768) { @@ -3533,11 +3535,12 @@ static void __attribute__((destructor)) fio_lib_destroy(void) { fio_data->active = 0; fio_on_fork(); fio_defer_perform(); + fio_timer_clear_all(); + fio_defer_perform(); fio_state_callback_force(FIO_CALL_AT_EXIT); fio_state_callback_clear_all(); fio_defer_perform(); fio_poll_close(); - fio_timer_clear_all(); fio_free(fio_data); /* memory library destruction must be last */ fio_mem_destroy(); @@ -3811,15 +3814,16 @@ static void fio_worker_cleanup(void) { fio_force_close(fd2uuid(i)); } } - fio_defer_perform(); - fio_state_callback_force(FIO_CALL_ON_FINISH); + fio_timer_clear_all(); fio_defer_perform(); if (!fio_data->is_worker) { - fio_cluster_signal_children(); + kill(0, SIGINT); while (wait(NULL) != -1) ; } fio_defer_perform(); + fio_state_callback_force(FIO_CALL_ON_FINISH); + fio_defer_perform(); fio_signal_handler_reset(); if (fio_data->parent == getpid()) { FIO_LOG_INFO(" --- Shutdown Complete ---\n"); @@ -5125,7 +5129,7 @@ struct subscription_s { void *udata1; void *udata2; /** reference counter. */ - uintptr_t ref; + volatile uintptr_t ref; /** prevents the callback from running concurrently for multiple messages. */ fio_lock_i lock; fio_lock_i unsubscribed; @@ -6202,7 +6206,7 @@ static void fio_cluster_listen_on_close(intptr_t uuid, (int)getpid()); #endif if (fio_data->active) - fio_stop(); + kill(0, SIGINT); } (void)uuid; } @@ -6244,6 +6248,7 @@ static void fio_cluster_client_handler(struct cluster_pr_s *pr) { break; case FIO_CLUSTER_MSG_SHUTDOWN: fio_stop(); + kill(getpid(), SIGINT); case FIO_CLUSTER_MSG_ERROR: /* fallthrough */ case FIO_CLUSTER_MSG_PING: /* fallthrough */ case FIO_CLUSTER_MSG_ROOT: /* fallthrough */ @@ -6498,7 +6503,7 @@ static void fio_pubsub_on_fork(void) { /** Signals children (or self) to shutdown) - NOT signal safe. */ static void fio_cluster_signal_children(void) { if (fio_parent_pid() != getpid()) { - fio_stop(); + kill(getpid(), SIGINT); return; } fio_cluster_server_sender(fio_msg_internal_create(0, FIO_CLUSTER_MSG_SHUTDOWN, diff --git a/ext/iodine/fio.h b/ext/iodine/fio.h index 03ceedfd..0f7b8c15 100644 --- a/ext/iodine/fio.h +++ b/ext/iodine/fio.h @@ -109,8 +109,8 @@ Version and helper macros #define FIO_VERSION_MAJOR 0 #define FIO_VERSION_MINOR 7 -#define FIO_VERSION_PATCH 0 -#define FIO_VERSION_BETA 9 +#define FIO_VERSION_PATCH 3 +#define FIO_VERSION_BETA 0 /* Automatically convert version data to a string constant - ignore these two */ #define FIO_MACRO2STR_STEP2(macro) #macro @@ -1250,7 +1250,7 @@ inline FIO_FUNC ssize_t fio_write(const intptr_t uuid, const void *buffer, inline FIO_FUNC ssize_t fio_sendfile(intptr_t uuid, intptr_t source_fd, off_t offset, size_t length) { return fio_write2(uuid, .data.fd = source_fd, .length = length, .is_fd = 1, - .offset = offset); + .offset = (uintptr_t)offset); } /** @@ -2984,8 +2984,8 @@ FIO_FUNC inline void fio_reschedule_thread(void) { /** Nanosleep the thread - a blocking throttle. */ FIO_FUNC inline void fio_throttle_thread(size_t nano_sec) { - const struct timespec tm = {.tv_nsec = (nano_sec % 1000000000), - .tv_sec = (nano_sec / 1000000000)}; + const struct timespec tm = {.tv_nsec = (long)(nano_sec % 1000000000), + .tv_sec = (time_t)(nano_sec / 1000000000)}; nanosleep(&tm, NULL); } @@ -5494,10 +5494,10 @@ Done * Note: FIO_SET_HASH_TYPE should, normaly be left alone (uintptr_t is * enough). Also, the hash value 0 is reserved to indicate an empty slot. * - * Note: the FIO_SET_OBJ_COMPARE for Sets or the FIO_SET_KEY_COMPARE will be - * used to compare against invalid as well as valid objects. Invalid - * objects have their bytes all zero. FIO_SET_*_DESTROY should somehow - * mark them as invalid. + * Note: the FIO_SET_OBJ_COMPARE or the FIO_SET_KEY_COMPARE will be used to + * compare against invalid as well as valid objects. Invalid objects have + * their bytes all zero. FIO_SET_*_DESTROY should somehow mark them as + * invalid. * * Note: Before freeing the Set, FIO_SET_OBJ_DESTROY will be automatically * called for every existing object. @@ -5610,16 +5610,16 @@ typedef struct { #endif /* The default Hash Map-Set has will use straight euqality operators */ -#if !defined(FIO_SET_KEY_COMPARE) +#ifndef FIO_SET_KEY_COMPARE #define FIO_SET_KEY_COMPARE(o1, o2) ((o1) == (o2)) #endif /** Internal macros for object actions in Hash mode */ #define FIO_SET_COMPARE(o1, o2) FIO_SET_KEY_COMPARE((o1).key, (o2).key) -#define FIO_SET_COPY(dest, org) \ +#define FIO_SET_COPY(dest, src) \ do { \ - FIO_SET_OBJ_COPY((dest).obj, (org).obj); \ - FIO_SET_KEY_COPY((dest).key, (org).key); \ + FIO_SET_OBJ_COPY((dest).obj, (src).obj); \ + FIO_SET_KEY_COPY((dest).key, (src).key); \ } while (0); #define FIO_SET_DESTROY(couplet) \ do { \ @@ -5871,7 +5871,7 @@ FIO_FUNC inline FIO_NAME(_map_s_) * if (FIO_SET_HASH_COMPARE(FIO_SET_HASH_INVALID, pos->hash)) return pos; if (FIO_SET_HASH_COMPARE(pos->hash, hash_value_i)) { - if (!pos->pos || FIO_SET_COMPARE(pos->pos->obj, obj)) + if (!pos->pos || (FIO_SET_COMPARE(pos->pos->obj, obj))) return pos; /* full hash value collision detected */ set->has_collisions = 1; @@ -5890,7 +5890,7 @@ FIO_FUNC inline FIO_NAME(_map_s_) * if (FIO_SET_HASH_COMPARE(FIO_SET_HASH_INVALID, pos->hash)) return pos; if (FIO_SET_HASH_COMPARE(pos->hash, hash_value_i)) { - if (!pos->pos || FIO_SET_COMPARE(pos->pos->obj, obj)) + if (!pos->pos || (FIO_SET_COMPARE(pos->pos->obj, obj))) return pos; /* full hash value collision detected */ set->has_collisions = 1; diff --git a/ext/iodine/fio_cli.c b/ext/iodine/fio_cli.c index bed978e6..f9f3d77b 100644 --- a/ext/iodine/fio_cli.c +++ b/ext/iodine/fio_cli.c @@ -272,19 +272,19 @@ static void fio_cli_set_arg(cstr_s arg, char const *value, char const *line, switch ((size_t)type) { case FIO_CLI_STRING__TYPE_I: fprintf(stderr, - " \x1B[1m%.*s\x1B[0m\x1B[2m <>\x1B[0m%*s\t\x1B[2msame as " - "%.*s\x1B[0m\n", + " \x1B[1m%.*s\x1B[0m\x1B[2m <>\x1B[0m%*s\t(same as " + "\x1B[1m%.*s\x1B[0m)\n", (int)(tmp - start), p + start, padding, "", first_len, p); break; case FIO_CLI_BOOL__TYPE_I: fprintf(stderr, - " \x1B[1m%.*s\x1B[0m %*s\t\x1B[2msame as %.*s\x1B[0m\n", + " \x1B[1m%.*s\x1B[0m %*s\t(same as \x1B[1m%.*s\x1B[0m)\n", (int)(tmp - start), p + start, padding, "", first_len, p); break; case FIO_CLI_INT__TYPE_I: fprintf(stderr, - " \x1B[1m%.*s\x1B[0m\x1B[2m ##\x1B[0m%*s\t\x1B[2msame as " - "%.*s\x1B[0m\n", + " \x1B[1m%.*s\x1B[0m\x1B[2m ##\x1B[0m%*s\t(same as " + "\x1B[1m%.*s\x1B[0m)\n", (int)(tmp - start), p + start, padding, "", first_len, p); break; } diff --git a/ext/iodine/fio_tls_missing.c b/ext/iodine/fio_tls_missing.c index 4dbbd9db..6c8b1114 100644 --- a/ext/iodine/fio_tls_missing.c +++ b/ext/iodine/fio_tls_missing.c @@ -19,7 +19,7 @@ Feel free to copy, use and enjoy according to the license provided. */ #include "fio_tls.h" -#if 1 /* TODO: place library compiler flags here */ +#if !defined(FIO_TLS_FOUND) /* Library compiler flags */ #define REQUIRE_LIBRARY() #define FIO_TLS_WEAK @@ -628,6 +628,7 @@ void FIO_TLS_WEAK fio_tls_destroy(fio_tls_s *tls) { fio_tls_destroy_context(tls); alpn_list_free(&tls->alpn); cert_ary_free(&tls->sni); + trust_ary_free(&tls->trust); free(tls); } diff --git a/ext/iodine/fio_tls_openssl.c b/ext/iodine/fio_tls_openssl.c index a68917c4..a49d838c 100644 --- a/ext/iodine/fio_tls_openssl.c +++ b/ext/iodine/fio_tls_openssl.c @@ -1005,6 +1005,7 @@ void FIO_TLS_WEAK fio_tls_destroy(fio_tls_s *tls) { fio_tls_destroy_context(tls); alpn_list_free(&tls->alpn); cert_ary_free(&tls->sni); + trust_ary_free(&tls->trust); free(tls); } diff --git a/ext/iodine/fiobj4fio.h b/ext/iodine/fiobj4fio.h index b03595e9..86415d9d 100644 --- a/ext/iodine/fiobj4fio.h +++ b/ext/iodine/fiobj4fio.h @@ -14,7 +14,7 @@ static inline __attribute__((unused)) ssize_t fiobj_send_free(intptr_t uuid, FIOBJ o) { fio_str_info_s s = fiobj_obj2cstr(o); return fio_write2(uuid, .data.buffer = (void *)(o), - .offset = (((intptr_t)s.data) - ((intptr_t)(o))), + .offset = (uintptr_t)(((intptr_t)s.data) - ((intptr_t)(o))), .length = s.len, .after.dealloc = fiobj4sock_dealloc); } diff --git a/ext/iodine/fiobj_numbers.h b/ext/iodine/fiobj_numbers.h index cae361b3..a805ad4b 100644 --- a/ext/iodine/fiobj_numbers.h +++ b/ext/iodine/fiobj_numbers.h @@ -82,10 +82,12 @@ size_t fio_ltoa(char *dest, int64_t num, uint8_t base); size_t fio_ftoa(char *dest, double num, uint8_t base); /** Converts a number to a temporary, thread safe, C string object */ -fio_str_info_s fio_ltocstr(long); +fio_str_info_s __attribute__((deprecated("use local buffer with fio_ltoa"))) +fio_ltocstr(long); /** Converts a float to a temporary, thread safe, C string object */ -fio_str_info_s fio_ftocstr(double); +fio_str_info_s __attribute__((deprecated("use local buffer with fio_ftoa"))) +fio_ftocstr(double); /* ***************************************************************************** Pointer Wrapping Helper MACROs (uses integers) diff --git a/ext/iodine/http.c b/ext/iodine/http.c index 19912617..18d7f4ae 100644 --- a/ext/iodine/http.c +++ b/ext/iodine/http.c @@ -356,6 +356,21 @@ int http_sendfile(http_s *r, int fd, uintptr_t length, uintptr_t offset) { return ((http_vtable_s *)r->private_data.vtbl) ->http_sendfile(r, fd, length, offset); } + +static inline int http_test_encoded_path(const char *mem, size_t len) { + const char *pos = NULL; + const char *end = mem + len; + while (mem < end && (pos = memchr(mem, '/', (size_t)len))) { + len = end - pos; + mem = pos + 1; + if (pos[1] == '/') + return -1; + if (len > 3 && pos[1] == '.' && pos[2] == '.' && pos[3] == '/') + return -1; + } + return 0; +} + /** * Sends the response headers and the specified file (the response's body). * @@ -391,14 +406,8 @@ int http_sendfile2(http_s *h, const char *prefix, size_t prefix_len, char *pos = (char *)encoded; const char *end = encoded + encoded_len; while (pos < end) { - /* test for path manipulations while decoding */ - if (*pos == '/' && (pos[1] == '/' || - (((uintptr_t)end - (uintptr_t)pos >= 4) && - pos[1] == '.' && pos[2] == '.' && pos[3] == '/'))) - return -1; if (*pos == '%') { - // decode hex value - // this is a percent encoded value. + // decode hex value (this is a percent encoded value). if (hex2byte((uint8_t *)tmp.data + tmp.len, (uint8_t *)pos + 1)) return -1; tmp.len++; @@ -408,6 +417,9 @@ int http_sendfile2(http_s *h, const char *prefix, size_t prefix_len, } tmp.data[tmp.len] = 0; fiobj_str_resize(filename, tmp.len); + /* test for path manipulations after decoding */ + if (http_test_encoded_path(tmp.data + prefix_len, tmp.len - prefix_len)) + return -1; } if (tmp.data[tmp.len - 1] == '/') fiobj_str_write(filename, "index.html", 10); diff --git a/ext/iodine/http.h b/ext/iodine/http.h index bf7dfa09..e13a8e20 100644 --- a/ext/iodine/http.h +++ b/ext/iodine/http.h @@ -370,7 +370,7 @@ struct http_settings_s { * sockets count towards a server's limit. */ intptr_t max_clients; - /** reserved for future SSL/TLS support. */ + /** SSL/TLS support. */ void *tls; /** reserved for future use. */ intptr_t reserved1; diff --git a/ext/iodine/http1.c b/ext/iodine/http1.c index f141c864..2aff665f 100644 --- a/ext/iodine/http1.c +++ b/ext/iodine/http1.c @@ -554,7 +554,7 @@ static int http1_on_request(http1_parser_s *parser) { if (p->request.method && !p->stop) http_finish(&p->request); h1_reset(p); - return !p->close && fio_is_closed(p->p.uuid); + return fio_is_closed(p->p.uuid); } /** called when a response was received. */ static int http1_on_response(http1_parser_s *parser) { @@ -563,7 +563,7 @@ static int http1_on_response(http1_parser_s *parser) { if (p->request.status_str && !p->stop) http_finish(&p->request); h1_reset(p); - return !p->close && fio_is_closed(p->p.uuid); + return fio_is_closed(p->p.uuid); } /** called when a request method is parsed. */ static int http1_on_method(http1_parser_s *parser, char *method, @@ -666,9 +666,9 @@ static int http1_on_body_chunk(http1_parser_s *parser, char *data, /** called when a protocol error occurred. */ static int http1_on_error(http1_parser_s *parser) { - FIO_LOG_DEBUG("HTTP parser error at HTTP/1.1 buffer position %zu/%zu", - parser->state.next - parser2http(parser)->buf, - parser2http(parser)->buf_len); + if (parser2http(parser)->close) + return -1; + FIO_LOG_DEBUG("HTTP parser error."); fio_close(parser2http(parser)->p.uuid); return -1; } @@ -723,8 +723,8 @@ static inline void http1_consume_data(intptr_t uuid, http1pr_s *p) { throttle: /* throttle busy clients (slowloris) */ - fio_suspend(uuid); p->stop |= 4; + fio_suspend(uuid); FIO_LOG_DEBUG("(HTTP/1,1) throttling client at %.*s", (int)fio_peer_addr(uuid).len, fio_peer_addr(uuid).data); } @@ -756,8 +756,8 @@ static void http1_on_close(intptr_t uuid, fio_protocol_s *protocol) { static void http1_on_ready(intptr_t uuid, fio_protocol_s *protocol) { /* resume slow clients from suspension */ http1pr_s *p = (http1pr_s *)protocol; - if ((p->stop & 4)) { - p->stop ^= 4; + if (p->stop & 4) { + p->stop ^= 4; /* flip back the bit, so it's zero */ fio_force_event(uuid, FIO_EVENT_ON_DATA); } (void)protocol; @@ -776,7 +776,6 @@ static void http1_on_data_first_time(intptr_t uuid, fio_protocol_s *protocol) { /* ensure future reads skip this first time HTTP/2.0 test */ p->p.protocol.on_data = http1_on_data; - /* Test fot HTTP/2.0 pre-knowledge */ if (i >= 24 && !memcmp(p->buf, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24)) { FIO_LOG_WARNING("client claimed unsupported HTTP/2 prior knowledge."); fio_close(uuid); diff --git a/ext/iodine/iodine_mustache.c b/ext/iodine/iodine_mustache.c index b31bb8b9..c391f5cd 100644 --- a/ext/iodine/iodine_mustache.c +++ b/ext/iodine/iodine_mustache.c @@ -73,6 +73,7 @@ static inline VALUE fiobj_mustache_find_obj_absolute(VALUE udata, /* search by String */ key = rb_sym2str(key); tmp = rb_hash_lookup2(udata, key, Qundef); + rb_str_free(key); if (tmp != Qundef) return tmp; /* search by method */ @@ -295,8 +296,6 @@ static VALUE iodine_mustache_new(int argc, VALUE *argv, VALUE self) { if (filename != Qnil) Check_Type(filename, T_STRING); - fio_str_s str = FIO_STR_INIT; - mustache_s **m = NULL; TypedData_Get_Struct(self, mustache_s *, &iodine_mustache_data_type, m); if (!m) { diff --git a/iodine.gemspec b/iodine.gemspec index 02223f14..87c175ff 100644 --- a/iodine.gemspec +++ b/iodine.gemspec @@ -42,5 +42,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest', '>=5', '< 6.0' spec.add_development_dependency 'rake-compiler', '>= 1', '< 2.0' - spec.post_install_message = "Thank you for installing Iodine #{Iodine::VERSION}.\n" + spec.post_install_message = "Thank you for installing Iodine #{Iodine::VERSION}.\n" + + "Remember: if iodine supports your business, it's is only fair to give value back (code contributions / donations)." end diff --git a/lib/iodine/version.rb b/lib/iodine/version.rb index 89ed25ef..6039f16a 100644 --- a/lib/iodine/version.rb +++ b/lib/iodine/version.rb @@ -1,3 +1,3 @@ module Iodine - VERSION = '0.7.33'.freeze + VERSION = '0.7.34'.freeze end