Skip to content

Fix wild pointer in map_key()/map_value() for malformed map_entry descriptors#27163

Open
jortles wants to merge 1 commit intoprotocolbuffers:mainfrom
jortles:fix-map-entry-wild-pointer
Open

Fix wild pointer in map_key()/map_value() for malformed map_entry descriptors#27163
jortles wants to merge 1 commit intoprotocolbuffers:mainfrom
jortles:fix-map-entry-wild-pointer

Conversation

@jortles
Copy link
Copy Markdown

@jortles jortles commented Apr 30, 2026

Summary

Descriptor::map_key() and map_value() use ABSL_DCHECK_EQ(field_count(), 2) to guard against invalid map_entry descriptors. Since ABSL_DCHECK is a no-op in Release builds (NDEBUG defined), a malformed map_entry descriptor with 0 fields causes field(1) to return a wild pointer. Subsequent calls to cpp_type() on this wild pointer read garbage and index kTypeToCppTypeMap out of bounds, producing a global-buffer-overflow.

Trigger path

  1. DescriptorPool::BuildFile() with AllowUnknownDependencies() accepts a FileDescriptorProto containing a message with options.map_entry = true and 0 fields
  2. DynamicMessageFactory::GetPrototype() + Message::ParseFromArray() on that message
  3. During IsInitialized(), ReflectionOps calls descriptor->map_value() which returns &fields_[1] on a 0-element array (wild pointer)
  4. field->cpp_type() reads garbage type_ from the wild pointer and indexes kTypeToCppTypeMap[garbage] out of bounds

Behavior by build type

Build NDEBUG ABSL_DCHECK Result
Debug not defined active Clean CHECK failure → SIGABRT
Release defined no-op Wild pointer → OOB read (undefined behavior)

Fix

  1. ABSL_DCHECK_EQABSL_CHECK_EQ in map_key() and map_value() so the invariant is enforced in all build configurations
  2. Validation in BuildMessage() to reject map_entry messages with field_count() != 2 at descriptor build time, before map_key()/map_value() can be called

Testing

Verified with a 95-byte crafted FileDescriptorProto input:

  • Before fix (Release+ASAN): AddressSanitizer: global-buffer-overflow in FieldDescriptor::cpp_type() at descriptor.h:3164
  • After fix: BuildFile() rejects the input with error "Map entry message must have exactly 2 fields (key and value)." — clean exit, no crash

@google-cla
Copy link
Copy Markdown

google-cla Bot commented Apr 30, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

…criptors

When BuildFile() with AllowUnknownDependencies() processes a
FileDescriptorProto containing a message marked as map_entry with 0
fields (instead of the required 2), map_value() returns field(1) on a
0-element array. The ABSL_DCHECK_EQ(field_count(), 2) guard is a no-op
in Release builds (NDEBUG defined), producing a wild pointer. Subsequent
calls to cpp_type() on this wild pointer read garbage from adjacent
memory and index kTypeToCppTypeMap out of bounds.

Fix:
1. Change ABSL_DCHECK_EQ to ABSL_CHECK_EQ in map_key() and map_value()
   so the invariant is enforced in all build configurations.
2. Add validation in BuildMessage() to reject map_entry messages that
   don't have exactly 2 fields, catching the problem at descriptor
   build time before map_key()/map_value() can be called.
@jortles jortles force-pushed the fix-map-entry-wild-pointer branch from ae83cef to 264c869 Compare April 30, 2026 03:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants