From 3505fe80667b9f1412662319e382612c084d4269ea436e64267035016a182e47 Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Thu, 5 Dec 2024 15:12:57 +0300 Subject: [PATCH] Protection from recursion DoS --- cyac/lib/dec.c | 3 +++ cyac/lib/err.c | 2 ++ cyac/lib/err.h | 3 +++ cyac/lib/items.c | 39 ++++++++++++++++++++++++++++++------- gyac/cmd/print/main.go | 2 +- gyac/dec.go | 40 +++++++++++++++++++++++++++++++++----- gyac/reflect.go | 2 +- gyac/yacpki/signed-data.go | 2 +- 8 files changed, 78 insertions(+), 15 deletions(-) diff --git a/cyac/lib/dec.c b/cyac/lib/dec.c index e7effca..f7f3438 100644 --- a/cyac/lib/dec.c +++ b/cyac/lib/dec.c @@ -143,6 +143,9 @@ YACAtomDecode( // NOLINT(misc-no-recursion) size_t binGot = 0; struct YACAtom bin; memset(&bin, 0, sizeof(struct YACAtom)); + if ((buf[1] & (unsigned char)YACAtomStrings) == 0) { + return YACErrIntNonBin; + } enum YACErr err = YACAtomDecode(&binGot, &bin, buf + 1, len - 1); if (err != YACErrNo) { return err; diff --git a/cyac/lib/err.c b/cyac/lib/err.c index 2eb870b..414703b 100644 --- a/cyac/lib/err.c +++ b/cyac/lib/err.c @@ -46,6 +46,8 @@ YACErr2Str(const enum YACErr err) return "NoMem"; case YACErrUnsatisfiedSchema: return "UnsatisfiedSchema"; + case YACErrDeepRecursion: + return "DeepRecursion"; default: return "unknown"; } diff --git a/cyac/lib/err.h b/cyac/lib/err.h index 55898d7..d58fb33 100644 --- a/cyac/lib/err.h +++ b/cyac/lib/err.h @@ -39,6 +39,8 @@ // Not enough memory for allocation. // @item YACErrUnsatisfiedSchema // Unsatisfied structure's schema. +// @item YACErrDeepRecursion +// Too deep recursion involved during parsing. // @end table // @end deftp enum YACErr { @@ -63,6 +65,7 @@ enum YACErr { YACErrMapUnordered, YACErrNoMem, YACErrUnsatisfiedSchema, + YACErrDeepRecursion, }; // TEXINFO: YACErr2Str diff --git a/cyac/lib/items.c b/cyac/lib/items.c index 9ecdbde..84189cc 100644 --- a/cyac/lib/items.c +++ b/cyac/lib/items.c @@ -26,6 +26,7 @@ #include "items.h" static const ptrdiff_t yacItemsPoolGrowLen = 64; +static const size_t parseMaxRecursionDepth = 1024; enum YACErr YACItemsInit(struct YACItems *items) @@ -113,13 +114,18 @@ yacItemsAdd( return YACErrNo; } -enum YACErr -YACItemsParse( // NOLINT(misc-no-recursion) +static enum YACErr +yacItemsParse( // NOLINT(misc-no-recursion) struct YACItems *items, size_t *off, const unsigned char *buf, - const size_t len) + const size_t len, + const bool allowContainers, + const size_t recursionDepth) { + if (recursionDepth >= parseMaxRecursionDepth) { + return YACErrDeepRecursion; + } size_t item = items->len; enum YACErr err = yacItemsAdd(items, off, buf, len); if (err != YACErrNo) { @@ -130,13 +136,16 @@ YACItemsParse( // NOLINT(misc-no-recursion) switch (items->list[item].atom.typ) { #pragma clang diagnostic pop case YACItemList: { + if (!allowContainers) { + return YACErrUnknownType; + } items->list[item].atom.v.list.head = item + 1; items->list[item].atom.v.list.len = 0; size_t prev = 0; size_t cur = 0; size_t idx = item; for (;;) { - err = YACItemsParse(items, off, buf, len); + err = yacItemsParse(items, off, buf, len, true, recursionDepth + 1); if (err != YACErrNo) { return err; } @@ -156,6 +165,9 @@ YACItemsParse( // NOLINT(misc-no-recursion) } } case YACItemMap: { + if (!allowContainers) { + return YACErrUnknownType; + } items->list[item].atom.v.list.head = item + 1; items->list[item].atom.v.list.len = 0; size_t idx = item; @@ -164,7 +176,7 @@ YACItemsParse( // NOLINT(misc-no-recursion) size_t prevKeyLen = 0; const unsigned char *prevKey = NULL; for (;;) { - err = YACItemsParse(items, off, buf, len); + err = yacItemsParse(items, off, buf, len, false, recursionDepth + 1); if (err != YACErrNo) { return err; } @@ -200,7 +212,7 @@ YACItemsParse( // NOLINT(misc-no-recursion) } prev = cur; idx = (items->len) - 1; - err = YACItemsParse(items, off, buf, len); + err = yacItemsParse(items, off, buf, len, true, recursionDepth + 1); if (err != YACErrNo) { return err; } @@ -215,6 +227,9 @@ YACItemsParse( // NOLINT(misc-no-recursion) } } case YACItemBlob: { + if (!allowContainers) { + return YACErrUnknownType; + } items->list[item].atom.v.blob.chunks = 0; const size_t chunkLen = items->list[item].atom.v.blob.chunkLen; size_t idx = item; @@ -222,7 +237,7 @@ YACItemsParse( // NOLINT(misc-no-recursion) size_t cur = 0; bool eoc = false; while (!eoc) { - err = YACItemsParse(items, off, buf, len); + err = yacItemsParse(items, off, buf, len, false, recursionDepth + 1); if (err != YACErrNo) { return err; } @@ -269,6 +284,16 @@ YACItemsParse( // NOLINT(misc-no-recursion) return YACErrNo; } +enum YACErr +YACItemsParse( // NOLINT(misc-no-recursion) + struct YACItems *items, + size_t *off, + const unsigned char *buf, + const size_t len) +{ + return yacItemsParse(items, off, buf, len, true, 0); +} + bool YACItemsEncode( // NOLINT(misc-no-recursion) const struct YACItems *items, diff --git a/gyac/cmd/print/main.go b/gyac/cmd/print/main.go index 93d9369..4469b54 100644 --- a/gyac/cmd/print/main.go +++ b/gyac/cmd/print/main.go @@ -15,7 +15,7 @@ func main() { if err != nil { log.Fatal(err) } - item, tail, err := gyac.DecodeItem(data) + item, tail, err := gyac.ItemDecode(data) if err != nil { log.Fatal(err) } diff --git a/gyac/dec.go b/gyac/dec.go index 3c94d73..7accd44 100644 --- a/gyac/dec.go +++ b/gyac/dec.go @@ -29,6 +29,8 @@ import ( type ItemType byte +const ParseMaxRecursionDepth = 1 << 10 + //go:generate stringer -type=ItemType const ( ItemEOC ItemType = iota @@ -187,6 +189,10 @@ func AtomDecode(buf []byte) (item *Item, off int, err error) { } var bin *Item var binOff int + if buf[1]&AtomStrings == 0 { + err = errors.New("wrong int value") + return + } bin, binOff, err = AtomDecode(buf[1:]) off += binOff if err != nil { @@ -311,7 +317,15 @@ func AtomDecode(buf []byte) (item *Item, off int, err error) { return } -func DecodeItem(buf []byte) (item *Item, tail []byte, err error) { +func itemDecode( + buf []byte, + allowContainers bool, + recursionDepth int, +) (item *Item, tail []byte, err error) { + if recursionDepth > ParseMaxRecursionDepth { + err = errors.New("deep recursion") + return + } var off int item, off, err = AtomDecode(buf) if err != nil { @@ -321,10 +335,14 @@ func DecodeItem(buf []byte) (item *Item, tail []byte, err error) { tail = buf switch item.Typ() { case ItemList: + if !allowContainers { + err = ErrUnknownType + return + } var sub *Item var v []*Item for { - sub, buf, err = DecodeItem(buf) + sub, buf, err = itemDecode(buf, true, recursionDepth+1) tail = buf if err != nil { tail = buf @@ -338,11 +356,15 @@ func DecodeItem(buf []byte) (item *Item, tail []byte, err error) { item.V = v return case ItemMap: + if !allowContainers { + err = ErrUnknownType + return + } v := make(map[string]*Item) var sub *Item var keyPrev string for { - sub, buf, err = DecodeItem(buf) + sub, buf, err = itemDecode(buf, false, recursionDepth+1) tail = buf if err != nil { return @@ -369,7 +391,7 @@ func DecodeItem(buf []byte) (item *Item, tail []byte, err error) { } keyPrev = s } - sub, buf, err = DecodeItem(buf) + sub, buf, err = itemDecode(buf, true, recursionDepth+1) tail = buf if err != nil { return @@ -383,12 +405,16 @@ func DecodeItem(buf []byte) (item *Item, tail []byte, err error) { item.V = v return case ItemBlob: + if !allowContainers { + err = ErrUnknownType + return + } chunkLen := int(item.V.(uint64)) v := &Blob{ChunkLen: chunkLen} var sub *Item BlobCycle: for { - sub, buf, err = DecodeItem(buf) + sub, buf, err = itemDecode(buf, false, recursionDepth+1) tail = buf if err != nil { return @@ -422,3 +448,7 @@ func DecodeItem(buf []byte) (item *Item, tail []byte, err error) { } return } + +func ItemDecode(buf []byte) (item *Item, tail []byte, err error) { + return itemDecode(buf, true, 0) +} diff --git a/gyac/reflect.go b/gyac/reflect.go index 1f5118d..27ad825 100644 --- a/gyac/reflect.go +++ b/gyac/reflect.go @@ -250,7 +250,7 @@ func MapToStruct(dst any, src map[string]any) error { func DecodeToStruct(dst any, raw []byte) (tail []byte, err error) { var item *Item - item, tail, err = DecodeItem(raw) + item, tail, err = ItemDecode(raw) if err != nil { return } diff --git a/gyac/yacpki/signed-data.go b/gyac/yacpki/signed-data.go index e440756..6c86e65 100644 --- a/gyac/yacpki/signed-data.go +++ b/gyac/yacpki/signed-data.go @@ -44,7 +44,7 @@ type SignedData struct { func SignedDataParse(data []byte) (sd *SignedData, tail []byte, err error) { var item *gyac.Item - item, tail, err = gyac.DecodeItem(data) + item, tail, err = gyac.ItemDecode(data) if err != nil { return } -- 2.50.0