protocolbuffers / protobuf

Protocol Buffers - Google's data interchange format
http://protobuf.dev
Other
65.16k stars 15.44k forks source link

RuntimeException in MessageSchema on Android with Proguard #6463

Closed MatFl closed 4 years ago

MatFl commented 5 years ago

Version: v3.9.0 Language: Java/Android Protoc gradle plugin 0.8.10 for java with option "lite"

When running an proguard obfuscated android app a Runtime Exception is thrown when trying to build a Proto with it's Builder.

java.lang.RuntimeException: Field fieldName_ for xxx.yyy.z.b$d not found. Known fields are [private java.lang.String xxx.yyy.z.b$d.h, ...]
        at com.google.e.ck.a(MessageSchema.java:608)
        at com.google.e.ck.a(MessageSchema.java:502)
        at com.google.e.ck.a(MessageSchema.java:221)
        at com.google.e.bz.a(ManifestSchemaFactory.java:85)
        at com.google.e.bz.a(ManifestSchemaFactory.java:71)
        at com.google.e.dc.a(Protobuf.java:93)
        at com.google.e.dc.c(Protobuf.java:107)
        at com.google.e.bh.M(GeneratedMessageLite.java:170)
        at com.google.e.bh$a.ab(GeneratedMessageLite.java:386)
        at com.google.e.bh$a.ac(GeneratedMessageLite.java:394)
        at xxx.yyy.a(XXX)

It seems than since 3.8.0 the new Javalite runtime uses reflection for the Schemas. I thought the lite should NOT be using any reflection at all.

m0hamed commented 5 years ago

Any updates on this? Is this a bug? going forward is javalite runtime always going to use reflection and not work with proguard/r8?

grill2010 commented 4 years ago

Is there any workaround for this? I use proguard in my project and I get the same error message. I know this happens because of accessing fields via reflection with hardcoded variable names but shouldn't it possible to tell proguard to just ignore these generated classes? I've already tried many things now in the proguatd-rules files but I'm not able to get it work. Maybe anyone could point me in the right direction?


Edit: adding the following line to your proguard-rules file helps to get rid of the error but keep in mind that your protobuf messages will then not be processed by proguard at all.

-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }

rafi-kamal commented 4 years ago

I've added a proguard config here as a workaround. Please let me know if it doesn't solve the problem.

MatFl commented 4 years ago

Using a proguard exception is a workaround AT MOST, but definitely not a solution! I still think reflection should not be used in lite variant at all.

rafi-kamal commented 4 years ago

I think this was needed to remove unused fields from a message. @donaldchai could you please add more context?

donaldchai commented 4 years ago

MessageSchema is used to avoid generating hashCode/equals/(de)serialization methods since a small schema would be more compact. Reflection is used to initialize the internal schema data structures the first time.

Removal of unused fields is not directly related.

I filed https://issuetracker.google.com/issues/144631039 for R8 to support obfuscation.

jombie commented 4 years ago

I am not able to get around this when using above proguard rule: `-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite {

; }` Fails with message "Field [fieldName] for [messageClass.getName(]) not found. Known fields are list of [fields]" Is there any alternative or any other configuration that I should try.
juanpmarin commented 4 years ago

@jombie I'm running with the same problem, did you find out a solution?

thaidn commented 4 years ago

I am not able to get around this when using above proguard rule: -keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite { <fields>; } Fails with message "Field [fieldName] for [messageClass.getName(]) not found. Known fields are list of [fields]"

Is there any alternative or any other configuration that I should try.

The following config works for me:

-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }

ubarua123 commented 3 years ago

The proguard rule worked for me but increased my app size by 3 MBs 😞

kyze8439690 commented 3 years ago

I am not able to get around this when using above proguard rule: -keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite { <fields>; } Fails with message "Field [fieldName] for [messageClass.getName(]) not found. Known fields are list of [fields]" Is there any alternative or any other configuration that I should try.

The following config works for me:

-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }

This proguard rule raise a new exception:

java.lang.ArrayIndexOutOfBoundsException: length=11; index=11

downgrade com.google.protobuf:protobuf-javalite from 3.13.0 to 3.11.4 solve this issue

tucson-tom commented 3 years ago

Ack. What a lame bug. ProGuard is generally enabled on release builds but not debug builds by default in Android Studio, so the result is APK/App Bundles that use protobuf crash immediately on startup but debug versions are totally fine. Also, why is this issue closed? Seems like a critical bug.

Dimezis commented 3 years ago

@tucson-tom it's not a bug, and it's not critical. Proto Messages use reflection in some cases, which Proguard can't know about unless you describe it in its rules. So naturally it strips off those fields, methods and classes that it thinks are not used (but in fact are used via reflection).

Just add a Proguard rule and that's it, that's basically why those rules exist

FZambia commented 3 years ago

This issue caused lots of problems in my library, I am not a Java developer personally and could not reproduce issue for a long time. Now looking at the reason it seems to me that things are broken in Protobuf Java ecosystem. There should be at least a way for library developers to avoid asking library users to add proguard rules manually into their project. Maybe this way already exists?

skjolber commented 2 years ago

I'm having problems as well, as I'd like to keep the code obfuscated. It is possible to turn of the use of reflection?

ntoskrnl commented 2 years ago

This issue caused lots of problems in my library, I am not a Java developer personally and could not reproduce issue for a long time. Now looking at the reason it seems to me that things are broken in Protobuf Java ecosystem. There should be at least a way for library developers to avoid asking library users to add proguard rules manually into their project. Maybe this way already exists?

There is a way: Library should include pro guard rules in META-INF/proguard in their jar. It has been used by other open source libraries for years already.

skjolber commented 2 years ago

Seems the generator we're using is outputting code which is not designed to be obfuscated, so unless you're up for writing your own generator, disabling obfuscation with proguard rules is the only alternative.

nikiizvorski commented 2 years ago

We are having the same issue. The only solution we currently have is using the following rule in ProGuard which seems to work as expected. I believe this should be handled on the protobuf library. Here is the rules that work:

-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }

And here is the rule that generates the problem mentioned here:

-keep,allowobfuscation class * extends com.google.protobuf.GeneratedMessageLite { *; }

Another solution would be to keep the members of the classes but let them obfuscated like so:

-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite { *; }

and then adding a -dontnote toyourpackagewhereprotobufclassesare as long as they aren't a public class for the descriptors

Let us know if there would be a version that we could use. This only happens after we migrated from Lite version to the new javalite versions. I pretty much tested all without the RC versions so far.