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;
return "NoMem";
case YACErrUnsatisfiedSchema:
return "UnsatisfiedSchema";
+ case YACErrDeepRecursion:
+ return "DeepRecursion";
default:
return "unknown";
}
// 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 {
YACErrMapUnordered,
YACErrNoMem,
YACErrUnsatisfiedSchema,
+ YACErrDeepRecursion,
};
// TEXINFO: YACErr2Str
#include "items.h"
static const ptrdiff_t yacItemsPoolGrowLen = 64;
+static const size_t parseMaxRecursionDepth = 1024;
enum YACErr
YACItemsInit(struct YACItems *items)
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) {
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;
}
}
}
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;
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;
}
}
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;
}
}
}
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;
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;
}
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,
if err != nil {
log.Fatal(err)
}
- item, tail, err := gyac.DecodeItem(data)
+ item, tail, err := gyac.ItemDecode(data)
if err != nil {
log.Fatal(err)
}
type ItemType byte
+const ParseMaxRecursionDepth = 1 << 10
+
//go:generate stringer -type=ItemType
const (
ItemEOC ItemType = iota
}
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 {
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 {
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
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
}
keyPrev = s
}
- sub, buf, err = DecodeItem(buf)
+ sub, buf, err = itemDecode(buf, true, recursionDepth+1)
tail = buf
if err != nil {
return
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
}
return
}
+
+func ItemDecode(buf []byte) (item *Item, tail []byte, err error) {
+ return itemDecode(buf, true, 0)
+}
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
}
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
}