Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Storage GetBytesAsync crash for gzip-encoded file #986

Open
vnorilo opened this issue Mar 9, 2021 · 11 comments
Open

[Bug] Storage GetBytesAsync crash for gzip-encoded file #986

vnorilo opened this issue Mar 9, 2021 · 11 comments

Comments

@vnorilo
Copy link

vnorilo commented Mar 9, 2021

[REQUIRED] Please fill in the following fields:

  • Unity editor version: 2019.4.14f1
  • Firebase Unity SDK version: 6.15.2 (also tried 7.1.0)
  • Source you installed the SDK: Unity Package Manager
  • Problematic Firebase Component: Storage
  • Other Firebase Components in use: Auth, Database, RemoteConfig, Messaging
  • Additional SDKs you are using: none
  • Platform you are using the Unity editor on: Mac
  • Platform you are targeting: Android
  • Scripting Runtime:IL2CPP

[REQUIRED] Please describe the issue here:

Android client crashes when calling GetBytesAsync for a Storage reference that points to a gzip-encoded file. In editor, the method works fine and provides the compressed stream bytes.

Steps to reproduce:

What happened? How can we make the problem occur?

  • Upload and gzip-encode a file to Firebase storage with e.g. gsutil cp -Z <local-file> <gs://remote-file>
  • Call FirebaseStorage.DefaultInstance.GetReference(<remote-file>).GetBytesAsync(1000000)

On Android device, the call will always crash, even when the file is much, much smaller than the provided limit:

2021/03/09 10:16:11.359 6904 7586 Error StorageException StorageException has occurred.
2021/03/09 10:16:11.359 6904 7586 Error StorageException An unknown error occurred, please check the HTTP result code and inner exception for server response.
2021/03/09 10:16:11.359 6904 7586 Error StorageException  Code: -13000 HttpResult: 200
2021/03/09 10:16:11.359 6904 7586 Error StorageException The maximum allowed buffer size was exceeded.
2021/03/09 10:16:11.359 6904 7586 Error StorageException java.lang.IndexOutOfBoundsException: The maximum allowed buffer size was exceeded.
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at com.google.firebase.storage.internal.cpp.CppByteDownloader.doInBackground(CppByteDownloader.java:55)
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at com.google.firebase.storage.StreamDownloadTask.run(com.google.firebase:firebase-storage@@19.1.1:179)
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at com.google.firebase.storage.StorageTask.lambda$getRunnable$7(com.google.firebase:firebase-storage@@19.1.1:1072)
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at com.google.firebase.storage.StorageTask$$Lambda$12.run(Unknown Source:2)
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at java.lang.Thread.run(Thread.java:784)

My suspicion is that the Android client allocates the byte buffer based on file size in storage. However, the fetch fails to send accepts: gzip to the server, which should cause automatic decompression in Google Cloud Storage and the resulting uncompressed stream is too big to fit the buffer, whose size may be based on the compressed size.

@vnorilo vnorilo changed the title [Bug] [Bug] Storage GetBytesAsync crash for gzip-encoded file Mar 9, 2021
@vimanyu vimanyu added type: bug type: question needs-info Need information for the developer and removed new New issue. type: question type: bug labels Mar 12, 2021
@vimanyu
Copy link
Contributor

vimanyu commented Mar 12, 2021

Hi,
Thanks for creating this issue. I just tried this real quick in our Unity storage quickstart example and could not reproduce it.
Here are the steps I tried,

  • Unity 2020.1.7f1 and FirebaseStorage 7.1.0
  • Uploaded "test.txt", "test.zip" and "test.gz" (created with gzip -c test.txt > test.gz) to storage bucket via the Firebase console web interface.
  • Downloaded bytes for each of them in Editor and on my Android 8.0 device and I didn't see any crashes.

Could you verify if our quickstart example crashes for you on Android too? If yes, could you try enabling debug logging and running once again,

Firebase.LogLevel logLevel = Firebase.LogLevel.Debug;

@vnorilo
Copy link
Author

vnorilo commented Mar 12, 2021

Thank you for the reply! Did you also set the content encoding metadata to gzip for the gz stream? To be clear, it is the (correct) metadata that causes SDK to crash, there is no problem per se in storing gzipped streams without encoding metadata. I'm not sure if that can be done via the console web interface, hence I used gsutil cp -Z, which is the documented way of uploading gzip-encoded content to cloud storage. I will check the quickstart next week. Thanks again!

@google-oss-bot google-oss-bot added needs-attention Need Googler's attention and removed needs-info Need information for the developer labels Mar 12, 2021
@vimanyu
Copy link
Contributor

vimanyu commented Mar 15, 2021

I see. I didn't try setting the content encoding metadata though the file type is identified as "application/x-gzip
" in the console. It will be quicker for us to debug if we can reproduce this easily in our quickstarts.

@vimanyu vimanyu added needs-info Need information for the developer and removed needs-attention Need Googler's attention labels Mar 15, 2021
@vnorilo
Copy link
Author

vnorilo commented Mar 17, 2021

Hi @vimanyu ,

Instructions for reproducing the crash with the quickstart-unity project. You do not need to make any changes.

  • Upload file to storage with gzip encoding. One way to do this is to use gsutil cp -Z <local-file> <remote-file>.
  • You can verify that the file opens from Firebase console. In this instance the compressed file is automatically decompressed.
  • Launch quickstart-unity storage sample
  • Enter <remote-file> to the quickstart "Storage Location" field.
  • Press "Download Bytes"
  • Expected result: get the gzip-encoded bytestream

This works as expected in Editor, but produces a crash as shown on the top of this thread on Android. Tested with trunk on this repo and the linked Firebase SDK 7.1.0 for XCode 12 and dotnet4. Unity 2019.4.14f1. gsutil is from https://cloud.google.com/storage/docs/gsutil .

If you omit -Z from the gsutil upload command, the file is not encoded or compressed, and the setup works on both platforms. Gzip encoding is mandatory for us because it reduces some storage file sizes by a factor of 50:1, but because of this bug we cannot use the correct encoding metadata, and have to communicate encoding out-of-band.

@google-oss-bot google-oss-bot added needs-attention Need Googler's attention and removed needs-info Need information for the developer labels Mar 17, 2021
@cynthiajoan
Copy link
Contributor

Hi @vnorilo, I followed your repro steps, use gsutil with -Z to upload a file, and download bytes with no issue. Is there any other steps that I should pay attention to? How big is your file?

@cynthiajoan cynthiajoan added needs-info Need information for the developer and removed needs-attention Need Googler's attention labels Mar 22, 2021
@vnorilo
Copy link
Author

vnorilo commented Mar 23, 2021

Hi Cynthia! Thanks for picking this up. Perhaps it results from a combination of Firebase SDK and Android system. As I speculated on top of this thread, Google Cloud will automatically decompress bytestreams when the http request doesn't have accepts: gzip . If Firebase SDK allocates the buffer based on file size but receives an uncompressed stream, that would explain this behavior.

At this point all I can really do is to try to make sure all the details are correct. Here I've got two files, gs://musopia-donotuse/readme.md and gs://musopia-donotuse/readme-identity.md. The former is uploaded with -Z, the latter not. The files are in a public-readable sandbox bucket, so you could even try these urls on your end.

Metadata as per gsutil

gs://musopia-donotuse/readme.md:
    Creation time:          Wed, 17 Mar 2021 13:34:30 GMT
    Update time:            Wed, 17 Mar 2021 13:34:43 GMT
    Storage class:          REGIONAL
    Cache-Control:          no-transform
    Content-Encoding:       gzip
    Content-Length:         251
    Content-Type:           application/octet-stream
    Metadata:               
        firebaseStorageDownloadTokens:defce827-c865-4ad4-974d-5cacb0c1134d
    Hash (crc32c):          vMMJYw==
    Hash (md5):             8qD9Edh2x6aA9aC AV6/Fg==
    ETag:                   CIaejNe4t 8CEAI=
    Generation:             1615988070223622
    Metageneration:         2
gs://musopia-donotuse/readme-identity.md:
    Creation time:          Wed, 17 Mar 2021 13:35:28 GMT
    Update time:            Wed, 17 Mar 2021 13:35:53 GMT
    Storage class:          REGIONAL
    Content-Length:         374
    Content-Type:           application/octet-stream
    Metadata:               
        firebaseStorageDownloadTokens:fea51e87-425a-45f9-8ab1-8762a6aa6d29
    Hash (crc32c):          F4i 2w==
    Hash (md5):             3tXAmpXvDTWnVfcuy6AM2g==
    ETag:                   CPGc5/K4t 8CEAI=
    Generation:             1615988128337521
    Metageneration:         2

I've attached screenshots of the quickstart app on the crashing device and the device system info screen.

Crash downloading readme.md

Screenshot_20210323-085618

Success downloading readme-identity.md

(after correcting typoed url :P )

Screenshot_20210323-085908

Device

Screenshot_20210323-090043

Thanks again!

@google-oss-bot google-oss-bot added needs-attention Need Googler's attention and removed needs-info Need information for the developer labels Mar 23, 2021
@cynthiajoan cynthiajoan added type: bug and removed needs-attention Need Googler's attention labels Mar 24, 2021
@cynthiajoan
Copy link
Contributor

cynthiajoan commented Mar 24, 2021

Hi @vnorilo, I managed to repro the issue both with our unity testapp and android testapp, our team will be looking into solve the issue.

Update: in the next release of firebase-storage:19.2.2 will contain a potential fix for the issue. Let's wait for the release to get out and check whether it's fixed. Thanks for being patient.

@cynthiajoan cynthiajoan added the blocked-by-dependencies Issues which is blocked by native Android/iOS SDK, backend or third-party dependencies. label Mar 24, 2021
@schmidt-sebastian
Copy link

This might not need a fix in the Android Storage SDK, but we need to fix our documentation to state that -1 is a valid return value: firebase/firebase-android-sdk#2618

@chkuang-g
Copy link
Contributor

chkuang-g commented Apr 23, 2021

@schmidt-sebastian

Unity StorageReference.GetBytesAsync() API is calling into StorageReference.getStream(StreamDownloadTask.StreamProcessor processor) through C API

C code did not check StreamDownloadTask.getTotalByteCount() at all to throw any exception.

Judge by the user's callstack, this seem to be thrown from native Android SDK.

2021/03/09 10:16:11.359 6904 7586 Error StorageException java.lang.IndexOutOfBoundsException: The maximum allowed buffer size was exceeded.
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at com.google.firebase.storage.internal.cpp.CppByteDownloader.doInBackground(CppByteDownloader.java:55)
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at com.google.firebase.storage.StreamDownloadTask.run(com.google.firebase:firebase-storage@@19.1.1:179)
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at com.google.firebase.storage.StorageTask.lambda$getRunnable$7(com.google.firebase:firebase-storage@@19.1.1:1072)
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at com.google.firebase.storage.StorageTask$$Lambda$12.run(Unknown Source:2)
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
2021/03/09 10:16:11.359 6904 7586 Error StorageException 	at java.lang.Thread.run(Thread.java:784)

Perhaps it works with StorageReference.getBytes() and StorageReference.getStream() but not StorageReference.getStream(StreamDownloadTask.StreamProcessor processor) ?

@chkuang-g
Copy link
Contributor

chkuang-g commented Apr 23, 2021

Further dig:

The exception is thrown from this code, which is indeed part of C /Unity SDK. However, judged by the code, it does not seem to be an issue in C /Unity SDK. Here is the reason:

The user specified the maxDownloadSizeBytes is 1,000,000 byts = 1Mb. That is, this.cppBufferSize is 1,000,000.

FirebaseStorage.DefaultInstance.GetReference(<remote-file>).GetBytesAsync(1000000)

The gzip file provided by the user is 251 bytes, which is much less than 1,000,000.

This implies that stream.read(bytes, 0, bytes.length) still return non--1 value after the file is downloaded, and cause the total downloaded byte size to exceed maxDownloadSizeBytes specified by the user. This implies the issue is in StreamProcessor, a Firebase Android API.

This does not seem to be the same issue to firebase/firebase-android-sdk#2618

@schmidt-sebastian also mentioned that the backend does not provide the file size of gzip encoded file to the client API. Could that be the issue?

@chkuang-g
Copy link
Contributor

chkuang-g commented Apr 27, 2021

Update:

Tried Android testapp and it does not seems like anything odd about the value in bytesDownloaded. getTotalByteCount() returns -1 as @schmidt-sebastian mentioned in firebase/firebase-android-sdk#2618. But that should not cause the exception to be thrown in C SDK

The seems to plague both FirebaseStorage.GetBytesAsync() and FirebaseStorage.GetStreamAsync() but not FirebaseStorage.GetFileAsync().

The only workaround for this probably is to download the file to Android device using FirebaseStorage.GetFileAsync(), then read the file afterward.

Will need to further investigate into the issue.

@chkuang-g chkuang-g removed the blocked-by-dependencies Issues which is blocked by native Android/iOS SDK, backend or third-party dependencies. label Apr 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants