]> Cypherpunks repositories - keks.git/commitdiff
Protection from recursion DoS
authorSergey Matveev <stargrave@stargrave.org>
Thu, 5 Dec 2024 12:12:57 +0000 (15:12 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Thu, 5 Dec 2024 12:46:47 +0000 (15:46 +0300)
cyac/lib/dec.c
cyac/lib/err.c
cyac/lib/err.h
cyac/lib/items.c
gyac/cmd/print/main.go
gyac/dec.go
gyac/reflect.go
gyac/yacpki/signed-data.go

index e7effca9f6ee024a7166677902158a1033c32ec7748bc2065b71abce9d2d0bec..f7f343808dc897b959d65467889b4574bb9954e56b8b76fbb182e7f42f9d2b81 100644 (file)
@@ -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;
index 2eb870bb2e32e454c5447d897b72d63f7b0688d92347d0abffbbd7f52f35c217..414703b4db94e5631bcd40f0a4bc5cfde17c016590da56cc091bb593fcf113ed 100644 (file)
@@ -46,6 +46,8 @@ YACErr2Str(const enum YACErr err)
         return "NoMem";
     case YACErrUnsatisfiedSchema:
         return "UnsatisfiedSchema";
+    case YACErrDeepRecursion:
+        return "DeepRecursion";
     default:
         return "unknown";
     }
index 55898d764e9e208904c81d61e67a9263f9552efcb9fae4976056c86b615bf327..d58fb3338dd639f9a09a7d614699de5388edfdeb2965f51d05cc7c27527e3141 100644 (file)
@@ -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
index 9ecdbde8998b2413dad2e5286e694f5b8d583389ee534fad29b1b4dfdb7b7bdd..84189cca8b78696d400cda75df19330f04540329f1d5d687f818d6573d7acfd8 100644 (file)
@@ -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,
index 93d93694173e0958998ca79897929c485bb9f1dcd2754541d68c5aa694eccdce..4469b54d31fa7d75daef5f41241536bde5ac62696836a076bdff0187e4f00930 100644 (file)
@@ -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)
        }
index 3c94d730f3a7b996bb0c29121a43e753b7a5947abdd2650f609eae4e11dfbad8..7accd44eef49ebf9801dbbc093142bd3b646e8d90f9fcff63f6991335b34768d 100644 (file)
@@ -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)
+}
index 1f5118d3fc2820a62b24ea0ba45e6bbf5c01351e7e7956f8efa8e95012344fe2..27ad825b331be98e0bd0ec0d57da6af9b9f44d8d3010b51581ade7025ab9c0c0 100644 (file)
@@ -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
        }
index e44075645547687c8a4cb216cdba3d21afaa35fe32aa1edcff9232c67260aa32..6c86e65622128c6158fa57a1bfd44c86d68cb3923c249fbe59dbc42920f83ae3 100644 (file)
@@ -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
        }