Skip to content

Commit adf556f

Browse files
protobuf-github-botgoogleberg
authored andcommitted
Restore compatibility of runtime with pre-3.22.x gencode impacted by CVE-2022-3171
Generated code from this range is covered by CVE-2022-3171 and potentially vulnerable to a Denial of Service issue. JavaProto 4.x previously dropped compatibility with the potentially vulnerable generated code, having the behavior of: * The vulnerable generated code was source-incompatible with new runtime (would not compile when built from source) * The vulnerable generated code was ABI-incompatible with new runtime (when using a .class file compiled against old runtime, a NoSuchMethodException would be thrown at parse time). After this change, instead: * The vulnerable generated code is now source-compatible (will compile). The first time each potentially vulnerable type is parsed, an error message will be logged noting that potentially vulnerable generated code is in use and the name of the corresponding type. * Environment variables may be set to either throw an exception instead (-Dcom.google.protobuf.error_on_unsafe_pre22_gencode) or to entirely silence the logged messages (-Dcom.google.protobuf.use_unsafe_pre22_gencode) This change was made based on community feedback regarding the difficulty in identifying and quickly remediating stale gencode in their transitive dependencies weighed against a careful evaluation of the realistic risk exposure of DoS (with no risk of other concerns including information leak or RCE). We strongly recommend that any users who observe the log messages to regenerate the corresponding code with a newer protoc. We recommend that any security-conscious services opt into error_on_unsafe_pre22_gencode to preclude any risk of a Denial of Service surface area being exposed. A future release may flip the default behavior to error by default as a measure to further help the ecosystem avoid the Denial of Service risks. PiperOrigin-RevId: 790798112
1 parent 72abf95 commit adf556f

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

‎java/core/src/main/java/com/google/protobuf/GeneratedMessage.java‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@
3030
import java.util.ArrayList;
3131
import java.util.Arrays;
3232
import java.util.Collections;
33+
import java.util.HashSet;
3334
import java.util.Iterator;
3435
import java.util.List;
3536
import java.util.Map;
37+
import java.util.Set;
3638
import java.util.SortedMap;
3739
import java.util.TreeMap;
40+
import java.util.logging.Logger;
3841

3942
/**
4043
* All generated protocol message classes extend this class. This class implements most of the
@@ -52,6 +55,8 @@
5255
public abstract class GeneratedMessage extends AbstractMessage implements Serializable {
5356
private static final long serialVersionUID = 1L;
5457

58+
private static final Logger logger = Logger.getLogger(GeneratedMessage.class.getName());
59+
5560
/**
5661
* For testing. Allows a test to disable the optimization that avoids using field builders for
5762
* nested messages until they are requested. By disabling this optimization, existing tests can be
@@ -394,6 +399,59 @@ protected static IntList emptyIntList() {
394399
return IntArrayList.emptyList();
395400
}
396401

402+
static final String PRE22_GENCODE_SILENCE_PROPERTY =
403+
"com.google.protobuf.use_unsafe_pre22_gencode";
404+
static final String PRE22_GENCODE_ERROR_PROPERTY =
405+
"com.google.protobuf.error_on_unsafe_pre22_gencode";
406+
407+
static final String PRE22_GENCODE_VULNERABILITY_MESSAGE =
408+
"As of 2022/09/29 (release 21.7) makeExtensionsImmutable should not be called from protobuf"
409+
+ " gencode. If you are seeing this message, your gencode is vulnerable to a denial of"
410+
+ " service attack. You should regenerate your code using protobuf 25.6 or later. Use the"
411+
+ " latest version that meets your needs. However, if you understand the risks and wish"
412+
+ " to continue with vulnerable gencode, you can set the system property"
413+
+ " `-Dcom.google.protobuf.use_unsafe_pre22_gencode` on the command line to silence this"
414+
+ " warning. You also can set"
415+
+ " `-Dcom.google.protobuf.error_on_unsafe_pre22_gencode` to throw an error instead. See"
416+
+ " security vulnerability:"
417+
+ " https://github.com/protocolbuffers/protobuf/security/advisories/GHSA-h4h5-3hr4-j3g2";
418+
419+
protected static final Set<String> loggedPre22TypeNames =
420+
Collections.synchronizedSet(new HashSet<String>());
421+
422+
static void warnPre22Gencode(Class<?> messageClass) {
423+
if (System.getProperty(PRE22_GENCODE_SILENCE_PROPERTY) != null) {
424+
return;
425+
}
426+
String messageName = messageClass.getName();
427+
String vulnerabilityMessage =
428+
"Vulnerable protobuf generated type in use: "
429+
+ messageName
430+
+ "\n"
431+
+ PRE22_GENCODE_VULNERABILITY_MESSAGE;
432+
433+
if (System.getProperty(PRE22_GENCODE_ERROR_PROPERTY) != null) {
434+
throw new UnsupportedOperationException(vulnerabilityMessage);
435+
}
436+
437+
if (!loggedPre22TypeNames.add(messageName)) {
438+
return;
439+
}
440+
441+
logger.warning(vulnerabilityMessage);
442+
}
443+
444+
/**
445+
* This method only exists as a shim for pre-22 gencode. This function is a no-op other than
446+
* warning or throwing an error for messages that are not extendable.
447+
*
448+
* @throws UnsupportedOperationException if the {@link #PRE22_GENCODE_ERROR_PROPERTY} system
449+
* property is set.
450+
*/
451+
protected void makeExtensionsImmutable() {
452+
warnPre22Gencode(getClass());
453+
}
454+
397455
protected static LongList emptyLongList() {
398456
return LongArrayList.emptyList();
399457
}
@@ -1037,6 +1095,16 @@ public boolean isInitialized() {
10371095
return super.isInitialized() && extensionsAreInitialized();
10381096
}
10391097

1098+
/**
1099+
* This method only exists as a shim for pre-22 gencode (see {@link
1100+
* GeneratedMessage.warnPre22Gencode}.
1101+
*/
1102+
@Override
1103+
protected void makeExtensionsImmutable() {
1104+
GeneratedMessage.warnPre22Gencode(getClass());
1105+
extensions.makeImmutable();
1106+
}
1107+
10401108
/**
10411109
* Used by subclasses to serialize extensions. Extension ranges may be interleaved with field
10421110
* numbers, but we must write them in canonical (sorted by field number) order.

‎java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java‎

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1976,4 +1976,104 @@ public void getAllFields_repeatedFieldsAreNotMutable() {
19761976
builder.clearField(repeatedMsgField);
19771977
assertThat(list).hasSize(1);
19781978
}
1979+
1980+
private TestUtil.TestLogHandler setupLogger() {
1981+
TestUtil.TestLogHandler logHandler = new TestUtil.TestLogHandler();
1982+
Logger logger = Logger.getLogger(GeneratedMessage.class.getName());
1983+
logger.addHandler(logHandler);
1984+
logHandler.setLevel(Level.ALL);
1985+
return logHandler;
1986+
}
1987+
1988+
static class TestMessageBaseForPre22WarningsTests extends GeneratedMessage {
1989+
@Override
1990+
protected FieldAccessorTable internalGetFieldAccessorTable() {
1991+
return null;
1992+
}
1993+
1994+
@Override
1995+
protected Message.Builder newBuilderForType(BuilderParent parent) {
1996+
return null;
1997+
}
1998+
1999+
@Override
2000+
public Message.Builder newBuilderForType() {
2001+
return null;
2002+
}
2003+
2004+
@Override
2005+
public Message.Builder toBuilder() {
2006+
return null;
2007+
}
2008+
2009+
@Override
2010+
public Message getDefaultInstanceForType() {
2011+
return null;
2012+
}
2013+
}
2014+
2015+
@Test
2016+
public void generatedMessage_makeExtensionsImmutableShouldLog() {
2017+
TestUtil.TestLogHandler logHandler = setupLogger();
2018+
GeneratedMessage.loggedPre22TypeNames.clear();
2019+
2020+
class TestMessage1 extends TestMessageBaseForPre22WarningsTests {}
2021+
class TestMessage2 extends TestMessageBaseForPre22WarningsTests {}
2022+
2023+
TestMessage1 msg = new TestMessage1();
2024+
TestMessage2 msg2 = new TestMessage2();
2025+
2026+
msg.makeExtensionsImmutable();
2027+
List<LogRecord> logs = logHandler.getStoredLogRecords();
2028+
assertThat(logs).hasSize(1);
2029+
String message = logs.get(0).getMessage();
2030+
// The generated type
2031+
assertThat(message)
2032+
.contains(
2033+
"Vulnerable protobuf generated type in use: "
2034+
+ "com.google.protobuf.GeneratedMessageTest$1TestMessage1");
2035+
assertThat(message).contains(GeneratedMessage.PRE22_GENCODE_VULNERABILITY_MESSAGE);
2036+
assertThat(message).contains(GeneratedMessage.PRE22_GENCODE_SILENCE_PROPERTY);
2037+
2038+
// Subsequent calls for the same type do not log again.
2039+
msg.makeExtensionsImmutable();
2040+
assertThat(logHandler.getStoredLogRecords()).hasSize(1);
2041+
2042+
// A call on a second type does log for that type.
2043+
msg2.makeExtensionsImmutable();
2044+
assertThat(logHandler.getStoredLogRecords()).hasSize(2);
2045+
// And not again (only once per type).
2046+
msg2.makeExtensionsImmutable();
2047+
assertThat(logHandler.getStoredLogRecords()).hasSize(2);
2048+
}
2049+
2050+
@Test
2051+
public void extendableMessage_makeExtensionsImmutableShouldThrowWhenOptedIn() {
2052+
System.setProperty("com.google.protobuf.error_on_unsafe_pre22_gencode", "true");
2053+
GeneratedMessage.loggedPre22TypeNames.clear();
2054+
2055+
class TestMessage3 extends TestMessageBaseForPre22WarningsTests {}
2056+
TestMessage3 msg = new TestMessage3();
2057+
assertThrows(UnsupportedOperationException.class, msg::makeExtensionsImmutable);
2058+
2059+
// When opting into throwing, it should throw every time, not just the first time.
2060+
assertThrows(UnsupportedOperationException.class, msg::makeExtensionsImmutable);
2061+
assertThrows(UnsupportedOperationException.class, msg::makeExtensionsImmutable);
2062+
2063+
System.clearProperty("com.google.protobuf.error_on_unsafe_pre22_gencode");
2064+
}
2065+
2066+
@Test
2067+
public void extendableMessage_makeExtensionsImmutableShouldBeSilentWhenOptedIn() {
2068+
System.setProperty("com.google.protobuf.use_unsafe_pre22_gencode", "true");
2069+
TestUtil.TestLogHandler logHandler = setupLogger();
2070+
GeneratedMessage.loggedPre22TypeNames.clear();
2071+
2072+
class TestMessage4 extends TestMessageBaseForPre22WarningsTests {}
2073+
TestMessage4 msg = new TestMessage4();
2074+
msg.makeExtensionsImmutable();
2075+
assertThat(logHandler.getStoredLogRecords()).isEmpty();
2076+
2077+
System.clearProperty("com.google.protobuf.use_unsafe_pre22_gencode");
2078+
}
19792079
}

0 commit comments

Comments
 (0)