Crashing w/ epson2 scanners
(Filing as an issue because the problem comes from 431be580 and it needs some discussion)
I have a 100% reproducible crasher in skanlite:
- turn on my Epson Photo RX420 MFP; it's a scanner supported through the epson2 backend from SANE.
scanimage
works flawlessly. - start skanlite, wait for a moment while the first dialog -- is that "finding devices" ? -- shows and then disappears because there is only one scanner
- in basic options, change Scan mode from Gray to Color (or, for that matter, change any other option or click in the scan area selector)
- skanlite crashes.
With valgrind, I chased memory corruption all over; skanlite!6 (merged) for a small-but-unrelated thing in Skanlite itself. I have a half-dozen fixes for read-uninitialized-variable in SANE itself that I need to upstream, and a MR for ksane (shortly) with some other uninitialized reads. With all of those out of the way, valgrind tells me (some lines snipped):
==17667== Invalid read of size 8
==17667== at 0x1972E6E1: setvalue (epson2.c:1947)
==17667== by 0x1972E17F: sane_epson2_control_option (epson2.c:2058)
==17667== by 0x6CACEFC: sane_dll_control_option (dll.c:1339)
==17667== by 0x6CA6B60: sane_control_option (dll-s.c:40)
==17667== by 0x48D16F2: KSaneIface::KSaneBaseOption::writeData(void*) (invent/libksane/src/options/ksanebas
eoption.cpp:112)
==17667== by 0x48D61F4: KSaneIface::KSaneListOption::setValue(QString const&) (invent/libksane/src/options/
ksanelistoption.cpp:248)
==17667== by 0x48D5EE7: KSaneIface::KSaneListOption::setValue(QVariant const&) (invent/libksane/src/options
/ksanelistoption.cpp:96)
==17667== by 0x48D0078: KSaneIface::KSaneOption::setValue(QVariant const&) (invent/libksane/src/ksaneoption
.cpp:136)
==17667== Address 0xce64828 is 312 bytes inside a block of size 328 free'd
==17667== at 0x484ECDC: free (in /usr/local/libexec/valgrind/vgpreload_memcheck-amd64-freebsd.so)
==17667== by 0x1972BA28: free_devices (epson2.c:892)
==17667== by 0x1972BC3C: probe_devices (epson2.c:909)
==17667== by 0x1972BA90: sane_epson2_get_devices (epson2.c:951)
==17667== by 0x6CAC086: sane_dll_get_devices (dll.c:1098)
==17667== by 0x6CA6ABA: sane_get_devices (dll-s.c:21)
==17667== by 0x48AE0D1: KSaneIface::FindSaneDevicesThread::run() (invent/libksane/src/ksanefinddevicesthrea
d.cpp:59)
==17667== Block was alloc'd at
==17667== at 0x484C8A4: malloc (in /usr/local/libexec/valgrind/vgpreload_memcheck-amd64-freebsd.so)
==17667== by 0x1972C046: device_detect (epson2.c:681)
==17667== by 0x1972FD70: attach (epson2.c:767)
==17667== by 0x1972F9C7: attach_one_usb (epson2.c:786)
==17667== by 0x6CAED60: sanei_usb_find_devices (sanei_usb.c:2181)
==17667== by 0x1972F868: attach_one_config (epson2.c:838)
==17667== by 0x6CA8CF5: sanei_configure_attach (sanei_config.c:445)
==17667== by 0x1972BC57: probe_devices (epson2.c:911)
==17667== by 0x1972BA90: sane_epson2_get_devices (epson2.c:951)
==17667== by 0x6CAC086: sane_dll_get_devices (dll.c:1098)
==17667== by 0x6CA6ABA: sane_get_devices (dll-s.c:21)
==17667== by 0x48AE0D1: KSaneIface::FindSaneDevicesThread::run() (invent/libksane/src/ksanefinddevicesthread.cpp:59)
This is use-after-free. It looks like sane_get_devices()
is allocating, and then freeing, resources in one go -- but it's not. it is being called twice. Internally, in SANE backend/epson2.c
, the code looks like this (again, snipped):
SANE_Status
sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only)
{
// ...
probe_devices(local_only);
// ...
}
In turn, probe_devices()
calls free_devices()
. The epson2 backend has a static list of devices that it handles, and so the first time sane_get_devices()
is called, the static list is allocated and filled. libksane grabs the handle of the one scanner -- which points into that static list! -- and builds the UI. Then sane_get_devices()
is called again, and the static list -- which libksane holds a non-owning pointer to! -- is freed by the internals. A new list is allocated.
Commit 431be580 is the cause of the second call to sane_get_devices()
because it always runs the thread again -- previously, if there was already a device, it would skip re-running the thread. Perhaps it's only the epson2 backend that has this pattern of owning the device internally, and freeing it up on scan, and this doesn't affect other backends. I don't have more scanners to test with.
Locally, reverting that one commit fixes my crashers and gets me a working libksane (and hence, skanlite).