Prevent signed integer overflow in _upb_WireReader_SkipGroup. When `upb_Message_FindUnknown2` is called with a `depth_limit` of `INT_MIN` on a message containing an unknown group field, `_upb_WireReader_SkipGroup` evaluates `--depth_limit < 0`. Decrementing `INT_MIN` before the bounds check triggers signed integer overflow, wrapping around to `INT_MAX` when not compiled with UBSan and bypassing the recursion depth check. Check `depth_limit <= 0` in `_upb_WireReader_SkipGroup` before decrementing `depth_limit`, and add a regression unit test for `upb_Message_FindUnknown2` with `INT_MIN` on an unknown group field. PiperOrigin-RevId: 1000384592
diff --git a/upb/message/unknown_fields_test.cc b/upb/message/unknown_fields_test.cc index 4355928..64a9bb8 100644 --- a/upb/message/unknown_fields_test.cc +++ b/upb/message/unknown_fields_test.cc
@@ -12,6 +12,7 @@ #include <string.h> +#include <climits> #include <cstddef> #include <cstdint> #include <string> @@ -397,4 +398,20 @@ upb_Arena_Free(arena); } +TEST(GeneratedCode, FindUnknown2_NegativeDepthLimitOnGroup) { + upb_Arena* arena = upb_Arena_New(); + upb_test_ModelWithExtensions* msg = upb_test_ModelWithExtensions_new(arena); + + // Field 13 StartGroup (0x6b) followed by Field 13 EndGroup (0x6c). + const char group_bytes[] = "\x6b\x6c"; + ASSERT_TRUE(UPB_PRIVATE(_upb_Message_AddUnknown)( + UPB_UPCAST(msg), group_bytes, 2, arena, kUpb_AddUnknown_Copy)); + + upb_FindUnknownRet2 result = + upb_Message_FindUnknown2(UPB_UPCAST(msg), 13, INT_MIN); + EXPECT_EQ(kUpb_FindUnknown_ParseError, result.status); + + upb_Arena_Free(arena); +} + } // namespace
diff --git a/upb/wire/reader.c b/upb/wire/reader.c index 85cd386..8cdec41 100644 --- a/upb/wire/reader.c +++ b/upb/wire/reader.c
@@ -63,9 +63,10 @@ const char* UPB_PRIVATE(_upb_WireReader_SkipGroup)( const char* ptr, uint32_t tag, int depth_limit, upb_EpsCopyInputStream* stream) { - if (--depth_limit < 0) { + if (depth_limit <= 0) { return UPB_PRIVATE(upb_EpsCopyInputStream_ReturnError)(stream); } + depth_limit--; uint32_t end_group_tag = (tag & ~7ULL) | kUpb_WireType_EndGroup; while (!upb_EpsCopyInputStream_IsDone(stream, &ptr)) { uint32_t tag;