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

Crashes after a few quick scans #166

Closed
OverkillGuy opened this issue Mar 7, 2021 · 11 comments
Closed

Crashes after a few quick scans #166

OverkillGuy opened this issue Mar 7, 2021 · 11 comments

Comments

@OverkillGuy
Copy link

Hello! I'm using your great little app for attempting something crazy: data exfiltration via QR codes. See qrxfil for the project details, but suffice to say I am aiming to scan dozens of QR codes of ~1KB payload in quick succession.

Issue: App crashes (back to android home) after ~15 to 20 QR code scans (in ~20 seconds)

Expected behaviour: Scan hundreds of QR codes without crash (at a rate of 1 to 2 per second)

Reproduction steps

Initially using BinaryEye version 1.37.0, reproduced on 1.39.0 (F-Droid)

  • Generate a bunch of QR codes of ~1KB data (I used qrxfil on a 500KB file, creating ~500 numbered PNGs to scan)
  • Enable Scan Continously
  • In an image viewer, open the first QR code image
  • When a QR code is recognized (vibration), move to the next one
  • After a few dozen, Android shows homepage without an explanation

Conjectures
The crash seems to happen after I scan a few dozen codes, each of which shows a Toast containing the 1KB of decoded text (base64 strings), all overlapping with each other. It is possible the Toast creation logic gets an error when drawing one too many, doesn't handle it, and crashes?
If so, disabling the Toast of QR contents would make the issue disappear. Worth investigating?

Note
I have not captured any logs pointing to reason for homepage, and haven't done any investigation as to what's wrong, am just reporting the issue naively for now. Expect more digging.

Am happy providing logs if I can, and can share sample QR codes that
trigger issues.

@markusfisch
Copy link
Owner

markusfisch commented Mar 7, 2021

Hello, and thanks for submitting this issue! 👍 Off the top of my head, I have no idea what's going wrong here unfortunately. But this shouldn't happen for sure!

Regarding your guess about the Toast, I am not sure if this is the cause - at least I can't see how this could be the cause just by looking at the code. But disabling the Toast would sure be a good idea for that use case. Also, there's a 500ms delay after recognizing a code at the moment. Two things that should better be configurable for your use case I guess.

Unfortunately, I can't see anything related to the crash in Google's crash logs. And since it's not so easy to reproduce this crash, I'd really appreciate it if you could capture the logcat output. You'd need the adb tool for it (comes with Android Studio, the bare Android SDK, and may be available in your package manager, homebrew, apt, etc), and to enable "adb debugging" on your device.

Then you can do:

$ adb logcat

And look for errors. If you want to see the messages in color, you could use pidcat:

$ pidcat de.markusfisch.android.binaryeye

About your use case and your project: what do you do with all the scanned codes in Binary Eye? How do you export them?

Off topic, do you know QR Codes have a binary input mode? This way you could save up to 2,953 bytes in a single QR Code (also that would mean the QR Code would become harder to read because of its size). A quick and easy way to find a sweet spot would be to use qrencode (also available in your package manager) because it can produce QR Codes with binary content:

$ qrencode -8 -o outfile < infile

And Binary Eye can read barcodes with binary content, of course.

@OverkillGuy
Copy link
Author

OverkillGuy commented Mar 7, 2021

Right!

Logs: Culled a bit to be relevant time-wise. That's a lot of noise but not a lot of signal I can see. The back-to-homepage crash I witnessed was at 22:54:10, but may have crashed internally a few seconds earlier (timeouts, escalation of kill etc).

adb2.log

Best I can see is:

03-07 22:54:08.221   424  1080 E SurfaceFlinger: Failed to find layer (SurfaceView - de.markusfisch.android.binaryeye/de.markusfisch.android.binaryeye.activity.CameraActivity#0) in layer parent (no-parent).
03-07 22:54:08.227  3387 14521 E Camera2Client: notifyError: Error condition 0 reported by HAL, requestId -1
03-07 22:54:08.227  3851  4317 I ActivityManager: Process de.markusfisch.android.binaryeye (pid 20553) has died: fore TOP 
03-07 22:54:08.228  3851  4317 W ActivityManager: Force removing ActivityRecord{32d8097 u0 de.markusfisch.android.binaryeye/.activity.CameraActivity t18954}: app died, no saved state
03-07 22:54:08.239  3851  4036 W InputDispatcher: channel 'fa90e1c de.markusfisch.android.binaryeye/de.markusfisch.android.binaryeye.activity.CameraActivity (server)' ~ Consumer closed input channel or an error occurred.  events=0x9
03-07 22:54:08.239  3851  4036 E InputDispatcher: channel 'fa90e1c de.markusfisch.android.binaryeye/de.markusfisch.android.binaryeye.activity.CameraActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
03-07 22:54:08.298   424  1080 E BufferQueueProducer: [SurfaceView - de.markusfisch.android.binaryeye/de.markusfisch.android.binaryeye.activity.CameraActivity#0] queueBuffer: BufferQueue has been abandoned

I don't know where to go from there, but I am happy posting a few dozen Chunks for issue reproductibility.


Currently I export scans to file in semicolon CSVs, and do a little awk magic to export line-delimited decoded chunks, but I will switch to HTTP, which means my "receiver" can live-read chunks off scanner, show progress and report "missing chunk 365 and 450".

Regarding my project, I saw binary mode, and am excited to get to it, but my cheapo prototyping in bash had issue in decoder when exporting CSV of binary so I went with base64 as a stopgap, as that works everywhere and is readable as ASCII. I look forward to using binary QR code to triple my payload (currently 1KB of base64 ~=750 bytes decoded). I also noticed the big QR codes like version 40 was a lot more prone to decoding issues in binaryeye (finding EAN codebar instead, guessing something about a low-pass filter early in the pipeline?) so I limited myself to 1KB payload ~= QR version 26-ish?

Thank you for your interest in my crazy endeavour!

@doronbehar
Copy link

I too experience crashes - can't use the app at all:

ScreenRecord-2021-03-08-19-30-05.mp4

@markusfisch
Copy link
Owner

@doronbehar This is a most probably a known RenderScript issue 😬 Sorry for that!

I guess you installed the app with F-Droid? Because F-Droid's build is broken for some devices running Android 6 🙈 Find the details in #113

A possible workaround would be to install the app either from GitHub or Google Play.

@markusfisch
Copy link
Owner

@OverkillGuy Thanks a lot for the logcat! 👍 And yes, I know, it's a lot of noise 😬

I think the snippet you posted is just the aftermath of the real problem - before that there are a lot of camera errors 🤔 But I can't see exactly what's causing the error yet either.

So I took qrxfil and tried to reproduce the error by scanning a file from 43 parts. I tried two different devices (Pixel 2 on Android 11 and a Nextbit Robin on Android 7) and made multiple runs but the app didn't crash for me. I used the latest version from Google Play (1.39.0). May I ask what device and Android version you are experiencing the crashes on?

Now, to rule out the Toast hypothesis I just added a new setting to disable that Toast in when scanning continuously:
47f95fc

I don't know if you're interested, but if you could build and run the app from source, you could try that right away. You'd just need the Android SDK (or Android Studio). Then, it would be a simple as cloning the app and running make 😉 This should build and install the app if the Android SDK is available in $ANDROID_HOME. The debug app can be installed beside the release version, so you wouldn't loose anything.

If you're not interested in building the app yourself, I will happily publish a beta version for you to try that change.


PS:
Just because I have now taken a closer look at scripts/qrexfil, which I used to test the app, and because I couldn't stop myself from tinkering with the idea, here's my version of it 😬 I hope you can forgive me for having to fiddle with it. scripts/qrexfil doesn't work on BSD (where I'm on) and this was the reason I started playing with it 😉

So, this works with GNU and BSD tools and is zipping the file before it's transferred. Also I used the 8-bit mode I mentioned before to make the QR Codes have as few modules as possible.

#!/usr/bin/env bash
(($# < 1)) && {
	echo "usage: ${0##*/} FILE..."
	exit
}
for F
do
	[ -r "$F" ] || {
		echo "file not found: $F" >&2
		exit 1
	}
	OUTDIR="$F.qrxf"
	mkdir "$OUTDIR" || exit $?
	BASENAME=${F##*/}
	gzip -c "$F" | (
		cd "$OUTDIR" || exit $?
		split -b "${CHUNK_SIZE:-1024}"
		for CF in x*
		do
			(
				echo "$BASENAME"
				echo "$CF"
				cat "$CF"
			) | qrencode -8 -o "${CF}.png"
			rm "$CF"
		done
	)
done

Binary Eye would export binary data as a Hex Dump, which can be used with xxd to recover the file. Here's a simple test script that would use the script above (which I called just qrxf) to split and recombine a given file just like you'd do with data from Binary Eye:

#!/usr/bin/env bash
readonly TEST_FILE=${TEST_FILE:-payload}
[ -r "$TEST_FILE" ] ||
   dd if=/dev/urandom of="$TEST_FILE" count="${COUNT:-8}" || exit $?
sed -e 's/qrencode.*/xxd -p | tr -d "\\n"; echo/' qrxf |
   bash /dev/stdin "$TEST_FILE" | while read -r
do
   echo "$REPLY" | xxd -r -p | (
   	read -r BASENAME
   	CHUNK_DIR="${BASENAME}.qrxt"
   	[ -d "$CHUNK_DIR" ] || mkdir "$CHUNK_DIR" || exit $?
   	read -r CHUNKNAME
   	cat > "$CHUNK_DIR/$CHUNKNAME"
   )
done
rm -rf "${TEST_FILE}.qrxf"
for D in *.qrxt
do
   [ -d "$D" ] || continue
   RESTORED="restored_${D%.qrxt}"
   cat "$D/x"* | gzip -d > "$RESTORED"
   rm -rf "$D"
   diff "$TEST_FILE" "$RESTORED" && {
   	echo "$TEST_FILE successfully restored"
   	rm "$RESTORED"
   }
done

@OverkillGuy
Copy link
Author

Am running a Nexus 5X (from 2016, getting a bit senile, worried the root cause is running out of RAM somehow) on LineageOS 15.1 (Android Oreo 8.1) non-rooted with F-Droid package version 1.39.0.

Here is a full unredacted logcat, crash around 00:14:35. I mostly truncated before because it looked like logcat captured stuff from my local wifi to bluetooth MACs, but meh.

For completeness here is the 581 chunk file I tried to scan
qrxfil-0.1.0-amd64.deb.tar.gz, maybe the files matter...

Tried building, but the Ubuntu (pop OS) package android-sdk didn't work out easily so I gave up early (been a while since I tried Android package building, nice Makefile though!). If the log doesn't give hints, I will try harder with less clean install processes (the wget > file.sh && sudo ./file.sh solutions on Android guides, I shudder at the thought)


For the script, thanks for having a look, I'm just glad people are as excited as I am about breaking airgaps creatively. It does looks much simpler without base64, chunk integer identification, and more bash knowledge =)

Funny you mention compression, I created a few tickets to log bugs/ideas that were on my mind and nowhere else, including OverkillGuy/qrxfil#8 and OverkillGuy/qrxfil#10, those are indeed relevant to what you describe (I went for zstd in my mind though).

@markusfisch
Copy link
Owner

Thanks for the full logcat and the original chunk files. I just scanned all 581 chunks with a Nokia 5 (with 2 GB RAM, which is the same as your Nexus 5X has) on Android 9 without a crash. Hm. So it's probably not a memory issue.

Also, memory consumption should be stable over time - otherwise there would be a memory leak that I haven't noticed yet 😬 I will try more devices in the coming days and check for memory leaks just to be sure it's not a leak.

The last error before the crash in logcat comes from the SurfaceFlinger so maybe the crash has something to do with the Toasts.

And yes, I know installing the Android SDK on Ubuntu can be quite a hassle 😉 So I put the new version (1.40.0) in beta so you can try it without having to build the app (you can always go back from beta). This version, 1.40.0, should also be on F-Droid in a few hours, if you prefer to install it from F-Droid.

@doronbehar
Copy link

And yes, I know installing the Android SDK on Ubuntu can be quite a hassle wink So I put the new version (1.40.0) in beta so you can try it without having to build the app (you can always go back from beta). This version, 1.40.0, should also be on F-Droid in a few hours, if you prefer to install it from F-Droid.

I checked version 1.40.0 from here on GitHub and it ran fine for me. The F-Droid version is not updated to 1.40.0 yet.

@OverkillGuy
Copy link
Author

OverkillGuy commented Mar 12, 2021

Insight! Since F-Droid didn't update yet, I used the sideloaded Github release and tested again with my 581 chunks.

Just with the disabled "Toast" flag, I was able to scan over 95 chunks so far with 0 crash. All good.

When enabling the flag again (back to usual behaviour), I wasn't able to crash either but the Toasts were tiny now (spent some time horizontally scanning at first, which may have made OS truncate text?)

I was very confused, but crash wasn't triggered either with or without the flag, in this new 1.40.0 version!

But then as I was trying to add HTTP GET to a local server (python3 -m http.server) as target for the scan, I saw the toast is still used (despite flag) for showing HTTP GET result/url. This may count as oversight, but this was the key to me understanding this:

The Toast looked different.

Here's a screenshot of scanning with a toast in this new version (spent some time first in horizontal mode, where text got truncated, I expect it's the OS truncating this. Back to vertical made the toast stay short, and didn't crash)
Screenshot_20210312-010431_Binary_Eye

With HTTP GET URL containing the text, note how it covers EVERYTHING on the screen. This is what the scanning looked like normally in version << 1.40. This DID crash after a while.
Screenshot_20210312-010532_Binary_Eye

So. I am a bit crazed after all this, but this seems to hint at:

  • the fact that multiple Toasts cover most of the screen is involved in my crash (echoes nicely with a component called "SurfaceFlinger", dunno)

  • (I suspect that) The OS truncated the Toast due to orientation detection (horizontal didn't have text area required = truncate, carried over to vertical), avodiing issue just now in the new version with enabled Toasts, just a red herring.

  • Note that crash-or-no-crash-yet seemed to depend on how fast I scanned, so that when 3-4 scans were quick enough, the Toasts were overlapping and raised chance of crash.

  • Your local testing didn't hit my corner case crash because your Toasts must have been small (Were they? am I making sense? am I crazy? Who knows!?)

  • The fact that HTTP GET response is still shown as toast despite flag was a fortunate oversight, because it helped me make sense of this.

Let me know how much of what is written above is simply wrong, but I feel validated =D
Also, thank you for your patience in investigating this bug, including scanning almost 600 pointless QR codes from a stranger.

@markusfisch
Copy link
Owner

Very interesting indeed! So it seems to be the Toast after all! 🤔 And you're right, this really fits the picture with the SurfaceFlinger errors.

About the truncation - I added this in the last version (in this commit) because I didn't like the Toast to cover everything 😉 But I did it just for this Toast, when I really should have done in for all the Toasts Binary Eye is opening. That's why the HTTP answer isn't truncated yet. I'll fix this soon and truncate all the Toasts.

Also I guess I should add another setting to switch off the HTTP Toast too. This is unrelated to scanning continousely and there might be use cases where someone wants one but not the other.

And you're welcome! I'm always happy to help. And I also don't like it when my apps crash 😉

markusfisch added a commit that referenced this issue Mar 13, 2021
Ellipsize texts in Toasts after 128 characters to not clutter the UI
with too long texts.

There's at least one case where too long toast texts caused crashes:
#166
@OverkillGuy
Copy link
Author

Nice fixes, I'm closing this ticket now, since we've got both a likely root cause, and a fix in the pipes.

This was a fascinating course through Android debugging for me, and I'm you were enthusiast at my crazy project idea.
Thanks again for the prompt reaction and patience! Keep up the good work, BinaryEye is a great tool.

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

No branches or pull requests

3 participants