about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2017-03-05 01:10:54 +0000
committerEric Wong <e@80x24.org>2017-03-05 02:06:52 +0000
commit7d8dacbeb608cccc8e7a8c0fb940bef937c3ce93 (patch)
treeca26bba47fae8e87e854af44872e75f34c4698b8
parente498c2f58b4c5219cbefc2faee526a3056814397 (diff)
downloadkcar-7d8dacbeb608cccc8e7a8c0fb940bef937c3ce93.tar.gz
The actual struct we manage is 48 bytes, along with
40 bytes for object overhead.
-rw-r--r--ext/kcar/kcar.rl42
1 files changed, 32 insertions, 10 deletions
diff --git a/ext/kcar/kcar.rl b/ext/kcar/kcar.rl
index 4c66fb5..e015bdb 100644
--- a/ext/kcar/kcar.rl
+++ b/ext/kcar/kcar.rl
@@ -10,6 +10,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
+#include <limits.h>
 #include "c_util.h"
 
 static VALUE eParserError;
@@ -47,15 +48,15 @@ DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
 struct http_parser {
   int cs; /* Ragel internal state */
   unsigned int flags;
-  size_t mark;
-  size_t offset;
+  unsigned int mark;
+  unsigned int offset;
   union { /* these 2 fields don't nest */
-    size_t field;
-    size_t query;
+    unsigned int field;
+    unsigned int query;
   } start;
   union {
-    size_t field_len; /* only used during header processing */
-    size_t dest_offset; /* only used during body processing */
+    unsigned int field_len; /* only used during header processing */
+    unsigned int dest_offset; /* only used during body processing */
   } s;
   VALUE cont; /* Qfalse: unset, Qnil: ignored header, T_STRING: append */
   VALUE status; /* String or Qnil */
@@ -65,9 +66,21 @@ struct http_parser {
   } len;
 };
 
+static unsigned int ulong2uint(unsigned long n)
+{
+  unsigned int i = (unsigned int)n;
+
+  if (sizeof(unsigned int) != sizeof(unsigned long)) {
+    if ((unsigned long)i != n) {
+      rb_raise(rb_eRangeError, "too large to be 32-bit uint: %lu", n);
+    }
+  }
+  return i;
+}
+
 #define REMAINING (unsigned long)(pe - p)
-#define LEN(AT, FPC) (FPC - buffer - hp->AT)
-#define MARK(M,FPC) (hp->M = (FPC) - buffer)
+#define LEN(AT, FPC) (ulong2uint(FPC - buffer) - hp->AT)
+#define MARK(M,FPC) (hp->M = ulong2uint((FPC) - buffer))
 #define PTR_TO(F) (buffer + hp->F)
 #define STR_NEW(M,FPC) rb_str_new(PTR_TO(M), LEN(M, FPC))
 #define STRIPPED_STR_NEW(M,FPC) stripped_str_new(PTR_TO(M), LEN(M, FPC))
@@ -403,7 +416,7 @@ static void http_parser_execute(struct http_parser *hp,
 post_exec: /* "_out:" also goes here */
   if (hp->cs != http_parser_error)
     hp->cs = cs;
-  hp->offset = p - buffer;
+  hp->offset = ulong2uint(p - buffer);
 
   assert(p <= pe && "buffer overflow after parsing execute");
   assert(hp->offset <= len && "offset longer than length");
@@ -537,6 +550,12 @@ static VALUE chunked(VALUE self)
   return HP_FL_TEST(hp, CHUNKED) ? Qtrue : Qfalse;
 }
 
+static void check_buffer_size(long dlen)
+{
+  if (dlen > UINT_MAX)
+    rb_raise(rb_eRangeError, "headers too large to process (%ld bytes)", dlen);
+}
+
 /**
  * Document-method: headers
  * call-seq:
@@ -552,8 +571,10 @@ static VALUE chunked(VALUE self)
 static VALUE headers(VALUE self, VALUE hdr, VALUE data)
 {
   struct http_parser *hp = data_get(self);
+  long dlen = RSTRING_LEN(data);
 
-  http_parser_execute(hp, hdr, RSTRING_PTR(data), RSTRING_LEN(data));
+  check_buffer_size(dlen);
+  http_parser_execute(hp, hdr, RSTRING_PTR(data), dlen);
   VALIDATE_MAX_LENGTH(hp->offset, HEADER);
 
   if (hp->cs == http_parser_first_final ||
@@ -653,6 +674,7 @@ static VALUE filter_body(VALUE self, VALUE buf, VALUE data)
 
   dptr = RSTRING_PTR(data);
   dlen = RSTRING_LEN(data);
+  check_buffer_size(dlen);
 
   StringValue(buf);
   rb_str_modify(buf);