summaryrefslogtreecommitdiffstats
path: root/src/bencoding.c
diff options
context:
space:
mode:
authorAnton Luka Šijanec <anton@sijanec.eu>2022-11-30 21:47:30 +0100
committerAnton Luka Šijanec <anton@sijanec.eu>2022-11-30 21:47:30 +0100
commit6e7924ce8e68937429a1180e62d348ce2608e347 (patch)
tree2b95f7f0a5fd1e7e00eb47a167efa95a65181f15 /src/bencoding.c
parentnič preizkušenega, grem spat (diff)
downloadtravnik-6e7924ce8e68937429a1180e62d348ce2608e347.tar
travnik-6e7924ce8e68937429a1180e62d348ce2608e347.tar.gz
travnik-6e7924ce8e68937429a1180e62d348ce2608e347.tar.bz2
travnik-6e7924ce8e68937429a1180e62d348ce2608e347.tar.lz
travnik-6e7924ce8e68937429a1180e62d348ce2608e347.tar.xz
travnik-6e7924ce8e68937429a1180e62d348ce2608e347.tar.zst
travnik-6e7924ce8e68937429a1180e62d348ce2608e347.zip
Diffstat (limited to 'src/bencoding.c')
-rw-r--r--src/bencoding.c69
1 files changed, 48 insertions, 21 deletions
diff --git a/src/bencoding.c b/src/bencoding.c
index 2063bdc..11acee9 100644
--- a/src/bencoding.c
+++ b/src/bencoding.c
@@ -27,11 +27,14 @@ struct bencoding {
size_t valuelen; /**< length of string value. */
long int intvalue;
int index;
+ unsigned seqnr; /**< sequential number for bdecode_safe element counting */
const char * after; /**< internal, set to character after this bencoded element in input string, used by recursive bdecode */
};
/**
* frees the passed bencoding struct or performs no action if NULL was passed. caller should NULL the pointer to prevent reuse.
+ *
+ * possible stack overflow: freeing large lists, those with either a lot of elements or very deep nesting causes a stack overflow due to the deep recursion of this funtion. bdecode wrapper makes sure that the number of elements does not exceed a certain hardcoded number.
*/
void free_bencoding (struct bencoding * b) {
@@ -429,32 +432,21 @@ void bdetach (struct bencoding * elem) {
}
/**
- * bdecodes a bencoded structure from a string into a bencoding structure that must be free_bencodinged by the caller.
- *
- * nonstandard things: this parser allows for dict keys to be of any type, valuekey
+ * element count limited bdecode, see bdecode wrapper function for full description. additional params are described here though
*
- * @param len [in] * if set to -1, string is assumed to be correct and not NULL terminated, NULLs may be in strings.
- * - malicious strings may trigger reads past the end of the buffer, which may lead to undefined
- * behaviour, crashes (DoS) or leaks of content, stored in memory.
- * - if opts&terminate, another character will be written after the bencoded structure in memory if
- * that structure is a string. beware and have space allocated for it!
- * * if set to -2, string is assumed to be NULL terminated and no further reading will be done after the NULL.
- * - if such terminator breaks an incomplete element, the resulting structure may be incomplete, but
- * will be correct - for example valuelen of a misterminated string will correctly be shortened.
- * * if set to a positive number, reading will only be allowed up to that many characters.
- * - if the input string reads the end and the structure is incomplete, same thing as with -2 happens.
- * - if the structure ends cleanly (string length satisfied or end of list, dict or num found),
- * processing stops, no mather how many characters of len are left.
- * @param opts [in] sets options. do not set the type bits here, this is the same enum as the ->type enum of returned struct.
- * opts will be reflected in the ->type of the returning struct. opts will apply to childs of lists&dicts too.
+ * @param seqnr [in] sequential count of this invocation
+ * @param max [in] max invocation count that can be achieved (max nr. of elems)
*/
-struct bencoding * bdecode (const char * s, int len, enum benc opts) {
+struct bencoding * bdecode_safe (const char * s, int len, enum benc opts, unsigned seqnr, unsigned max) {
+ if (seqnr >= max)
+ return NULL;
if (!s || len < -2 || (len >= 0 && len < 2 /* 2 being the smallest bencoding string */))
return NULL;
if (len == -2)
len = strlen(s);
struct bencoding * b = calloc(1, sizeof(struct bencoding)); /* SEGV if OOM */
+ b->seqnr = seqnr;
char * ch = NULL;
switch (s[0]) {
case 'i': /* num */
@@ -483,11 +475,16 @@ struct bencoding * bdecode (const char * s, int len, enum benc opts) {
struct bencoding * oldoldarbeit = NULL; /* for dicts, holds previous value */
int index = 0;
while (len == -1 || cp <= s+len) { /* s+len is max we are allowed to read */
- arbeit = bdecode(cp, len == -1 ? -1 : len-(cp-s), opts);
- if (arbeit)
+ arbeit = bdecode_safe(cp, len == -1 ? -1 : len-(cp-s), opts, ++b->seqnr, max);
+ if (arbeit) {
arbeit->parent = b;
- if (!arbeit) /* bdecoding failed or last element */
+ b->seqnr = arbeit->seqnr;
+ } else
+ break;
+ if (b->seqnr >= max) {
+ free_bencoding(arbeit);
break;
+ }
#define ISDICT (b->type & dict)
#define ISLIST !ISDICT
#define ISVAL (index % 2)
@@ -547,6 +544,33 @@ struct bencoding * bdecode (const char * s, int len, enum benc opts) {
}
/**
+ * bdecodes a bencoded structure from a string into a bencoding structure that must be free_bencodinged by the caller.
+ *
+ * nonstandard things: this parser allows for dict keys to be of any type, valuekey
+ *
+ * this is a wrapper function, the implementation is in bdecode_safe that was made as an afterthought to prevent stack overflows and limits the number of elements bdecoded to 2**16.
+ *
+ * @param len [in] * if set to -1, string is assumed to be correct and not NULL terminated, NULLs may be in strings.
+ * - malicious strings may trigger reads past the end of the buffer, which may lead to undefined
+ * behaviour, crashes (DoS) or leaks of content, stored in memory.
+ * - if opts&terminate, another character will be written after the bencoded structure in memory if
+ * that structure is a string. beware and have space allocated for it!
+ * * if set to -2, string is assumed to be NULL terminated and no further reading will be done after the NULL.
+ * - if such terminator breaks an incomplete element, the resulting structure may be incomplete, but
+ * will be correct - for example valuelen of a misterminated string will correctly be shortened.
+ * * if set to a positive number, reading will only be allowed up to that many characters.
+ * - if the input string reads the end and the structure is incomplete, same thing as with -2 happens.
+ * - if the structure ends cleanly (string length satisfied or end of list, dict or num found),
+ * processing stops, no mather how many characters of len are left.
+ * @param opts [in] sets options. do not set the type bits here, this is the same enum as the ->type enum of returned struct.
+ * opts will be reflected in the ->type of the returning struct. opts will apply to childs of lists&dicts too.
+ */
+
+struct bencoding * bdecode (const char * s, int len, enum benc opts) {
+ return bdecode_safe(s, len, opts, 0, 65535);
+}
+
+/**
* returns a pointer to bencoding struct matching bencoding path or NULL if not found.
*
* path key/key2/key3 will given object {"key":{"key2":{"key3":val}}} return val
@@ -666,6 +690,8 @@ int bencode_length (struct bencoding * b) {
* @return the pointer to the byte after the last written byte
*/
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstringop-truncation"
char * bencode (char * dest, struct bencoding * b) {
if (!b)
return dest;
@@ -699,6 +725,7 @@ char * bencode (char * dest, struct bencoding * b) {
}
return dest;
}
+#pragma GCC diagnostic pop
/**
* clones a bencoding object including all of it's children