diff options
author | Anton Luka Šijanec <anton@sijanec.eu> | 2022-11-30 21:47:30 +0100 |
---|---|---|
committer | Anton Luka Šijanec <anton@sijanec.eu> | 2022-11-30 21:47:30 +0100 |
commit | 6e7924ce8e68937429a1180e62d348ce2608e347 (patch) | |
tree | 2b95f7f0a5fd1e7e00eb47a167efa95a65181f15 /src/bencoding.c | |
parent | nič preizkušenega, grem spat (diff) | |
download | travnik-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.c | 69 |
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 |