Skip to content

Reject [weak = true] on non-message fields to prevent SEGV#27169

Open
jortles wants to merge 1 commit intoprotocolbuffers:mainfrom
jortles:fix-weak-nonmessage-segv
Open

Reject [weak = true] on non-message fields to prevent SEGV#27169
jortles wants to merge 1 commit intoprotocolbuffers:mainfrom
jortles:fix-weak-nonmessage-segv

Conversation

@jortles
Copy link
Copy Markdown

@jortles jortles commented Apr 30, 2026

Summary

A crafted FileDescriptorProto with weak=true on a scalar field (e.g., TYPE_DOUBLE) causes a SEGV (wild pointer dereference) when the resulting DynamicMessage is used with ParseFromArray. This is a DoS vulnerability affecting any application that builds descriptors from untrusted input with AllowUnknownDependencies() (gRPC server reflection, schema registries, protoc, dynamic config systems).

Root cause

  • weak=true on a non-message field causes GetFieldHasbitModeWithoutProfile() to return kNoHasbit (descriptor.cc:10751)
  • When DynamicMessage::ParseFromArray() lazily generates the TcParseTable via Reflection::CreateTcParseTable(), the kNoHasbit hasbit index produces incorrect field offsets in the table entries
  • TcParser writes the parsed field value at the wrong offset, corrupting DynamicMessage's internal type_info_ pointer
  • Post-parse calls to GetMetadata() / GetDescriptor() / IsInitialized() dereference the corrupted pointer → SEGV

Crash site

#0 Message::GetMetadata()         message.cc:430
#1 Message::GetDescriptor()       message.h:377
#2 TcParser::ReflectionFallback() generated_message_tctable_full.cc:69
#3 TcParser::TagDispatch()        generated_message_tctable_impl.h:1163
#4 TcParser::ParseLoop()          generated_message_tctable_impl.h:1205
#5 MergeFromImpl<false>()         message_lite.cc:227

Fix

Add validation in ValidateOptions() to reject [weak = true] on non-message fields, following the identical pattern already used for [lazy = true] (line 8581). The [weak] option is only meaningful for message-type fields (cross-file dependency management) and has no valid use on scalar, enum, or group fields.

Reproduction

google::protobuf::FileDescriptorProto fdp;
fdp.set_name("test.proto");
fdp.set_syntax("proto2");
auto* msg = fdp.add_message_type();
msg->set_name("Msg");
auto* f = msg->add_field();
f->set_name("f");
f->set_number(1);
f->set_type(google::protobuf::FieldDescriptorProto::TYPE_DOUBLE);
f->set_label(google::protobuf::FieldDescriptorProto::LABEL_REPEATED);
f->mutable_options()->set_weak(true);  // ← weak on non-message

google::protobuf::DescriptorPool pool;
pool.AllowUnknownDependencies();
auto* fd = pool.BuildFile(fdp);    // succeeds (no validation)
google::protobuf::DynamicMessageFactory factory;
auto* proto = factory.GetPrototype(fd->message_type(0));
auto msg1 = std::unique_ptr<google::protobuf::Message>(proto->New());
uint8_t data[] = {0x09,0,0,0,0,0,0,0xf0,0x3f};  // field 1, double 1.0
msg1->ParseFromArray(data, sizeof(data));  // SEGV

Found by AFL++ fuzzing with custom harness targeting BuildFile + DynamicMessageFactory pipeline.

…Message

A crafted FileDescriptorProto with weak=true on a scalar field (e.g.,
TYPE_DOUBLE) causes a SEGV when the resulting DynamicMessage is used
with ParseFromArray. The crash occurs because weak fields are assigned
HasbitMode::kNoHasbit, which produces incorrect field offsets in the
lazily-generated TcParseTable. TcParser then writes field data at the
wrong offset, corrupting the DynamicMessage's internal type_info_
pointer, leading to a wild pointer dereference in GetMetadata().

The [weak] option is only meaningful for message-type fields (for
cross-file dependency management). Add validation in ValidateOptions
to reject [weak = true] on non-message fields, matching the existing
pattern for [lazy = true].
@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.

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