There was a race condition where a context might be
registered before the netreg status updates to LTE.
The code took for granted the context is activated after
the technology update. With this change, any order is
is accepted.
Use at_util_sim_state_query_new() to query the sim inserted state. Once
that returns, the locked state is queried by issuing a AT+CPIN? command.
If not locked, a timer is started to query the quectel init status of
the sim. Once the init status is ready, the sim atom is created, and the
modem is set to powered, and the sim is signaled both inserted, and
initialized.
If locked, the modem is set to powered, and the sim atom is created.
This allows users to enter the pin to unlock the sim. Once the sim is
unlocked, a +CPIN: READY indication is caught to query the quectel
init status. Once the init status is ready, the sim is signaled
initialized.
All the above is needed, because the modem indicated +CPIN: READY before
the sim is really ready. The only way to be certain, is to wait for the
quectel init status to be ready. Even signaling the sim inserted
prematurely can cause to modem to hang during the initial AT+CRSM
commands.
With the reference in place in received_data(), the address sanitizer
now encounters a use-after-free when the destroy notification is
dispatched for the read watcher (see below).
Fix this by remove the destroy notification callback, as it isn't really
used except in the shutdown function.
==5797==ERROR: AddressSanitizer: heap-use-after-free on address 0x621000ac5904 at pc 0x55c1243b1f14 bp 0x7ffdef001340 sp 0x7ffdef001330
WRITE of size 4 at 0x621000ac5904 thread T0
#0 0x55c1243b1f13 in read_watcher_destroy_notify ../git/gatchat/gatmux.c:660
#1 0x7f08a8676742 (/usr/lib/libglib-2.0.so.0+0x62742)
#2 0x7f08a867e2e4 in g_main_context_dispatch (/usr/lib/libglib-2.0.so.0+0x6a2e4)
#3 0x7f08a8680210 (/usr/lib/libglib-2.0.so.0+0x6c210)
#4 0x7f08a8681122 in g_main_loop_run (/usr/lib/libglib-2.0.so.0+0x6d122)
#5 0x55c1243d6703 in main ../git/src/main.c:286
#6 0x7f08a8423152 in __libc_start_main (/usr/lib/libc.so.6+0x27152)
#7 0x55c1241fe1ad in _start (/home/martin/projects/ofono/x86/src/ofonod+0xfd1ad)
0x621000ac5904 is located 4 bytes inside of 4672-byte region [0x621000ac5900,0x621000ac6b40)
freed by thread T0 here:
#0 0x7f08a88cc6b0 in __interceptor_free /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:122
#1 0x55c1243b1ebf in g_at_mux_unref ../git/gatchat/gatmux.c:652
#2 0x55c1243b062c in received_data ../git/gatchat/gatmux.c:276
#3 0x7f08a867e2ce in g_main_context_dispatch (/usr/lib/libglib-2.0.so.0+0x6a2ce)
previously allocated by thread T0 here:
#0 0x7f08a88cccd8 in __interceptor_calloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:153
#1 0x55c1243b1bf1 in g_at_mux_new ../git/gatchat/gatmux.c:613
#2 0x55c1243b4b53 in g_at_mux_new_gsm0710_basic ../git/gatchat/gatmux.c:1172
#3 0x55c124386abd in cmux_gatmux ../git/plugins/quectel.c:871
#4 0x55c12438779f in cmux_cb ../git/plugins/quectel.c:1023
#5 0x55c1243a368e in at_chat_finish_command ../git/gatchat/gatchat.c:459
#6 0x55c1243a3bc8 in at_chat_handle_command_response ../git/gatchat/gatchat.c:521
#7 0x55c1243a4408 in have_line ../git/gatchat/gatchat.c:600
#8 0x55c1243a539e in new_bytes ../git/gatchat/gatchat.c:759
#9 0x55c1243ae2f9 in received_data ../git/gatchat/gatio.c:122
#10 0x7f08a867e2ce in g_main_context_dispatch (/usr/lib/libglib-2.0.so.0+0x6a2ce)
SUMMARY: AddressSanitizer: heap-use-after-free ../git/gatchat/gatmux.c:660 in read_watcher_destroy_notify
Shadow bytes around the buggy address:
0x0c4280150ad0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4280150ae0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4280150af0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4280150b00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c4280150b10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c4280150b20:[fd]fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c4280150b30: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c4280150b40: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c4280150b50: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c4280150b60: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c4280150b70: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==5797==ABORTING
When closing down a cmux object, the address sanitizer detects a
use-after-free in gatmux.c (see below).
Avoid this by taking a reference to the mux object during the processing
in received_data().
ofonod[3640549]: ../git/plugins/quectel.c:cfun_disable() 0x610000000b40
ofonod[3640549]: ../git/plugins/quectel.c:close_serial() 0x610000000b40
ofonod[3640549]: ../git/plugins/quectel.c:close_mux() 0x610000000b40
ofonod[3640549]: ../git/examples/emulator.c:powered_watch() Removing modem 0x610000000b40 from the list
ofonod[3640549]: ../git/examples/emulator.c:powered_watch() Removing server watch: 106
ofonod[3640549]: ../git/src/modem.c:modem_change_state() old state: 0, new state: 0
=================================================================
==3640549==ERROR: AddressSanitizer: heap-use-after-free on address 0x62100073dd28 at pc 0x5566b6402a21 bp 0x7ffe7a2db0e0 sp 0x7ffe7a2db0d0
READ of size 8 at 0x62100073dd28 thread T0
#0 0x5566b6402a20 in debug ../git/gatchat/gatmux.c:109
#1 0x5566b6404bd7 in channel_close ../git/gatchat/gatmux.c:525
#2 0x7fa0516e44a6 in g_io_channel_shutdown (/usr/lib/libglib-2.0.so.0+0x774a6)
#3 0x7fa0516e4644 in g_io_channel_unref (/usr/lib/libglib-2.0.so.0+0x77644)
#4 0x5566b64048a4 in watch_finalize ../git/gatchat/gatmux.c:474
#5 0x7fa0516d6f6f (/usr/lib/libglib-2.0.so.0+0x69f6f)
#6 0x7fa0516ac6a7 in g_slist_foreach (/usr/lib/libglib-2.0.so.0+0x3f6a7)
#7 0x7fa0516b277b in g_slist_free_full (/usr/lib/libglib-2.0.so.0+0x4577b)
#8 0x5566b6403413 in dispatch_sources ../git/gatchat/gatmux.c:224
#9 0x5566b64039ea in received_data ../git/gatchat/gatmux.c:268
#10 0x7fa0516d727e in g_main_context_dispatch (/usr/lib/libglib-2.0.so.0+0x6a27e)
#11 0x7fa0516d91c0 (/usr/lib/libglib-2.0.so.0+0x6c1c0)
#12 0x7fa0516da0d2 in g_main_loop_run (/usr/lib/libglib-2.0.so.0+0x6d0d2)
#13 0x5566b6429b1b in main ../git/src/main.c:286
#14 0x7fa05147fee2 in __libc_start_main (/usr/lib/libc.so.6+0x26ee2)
#15 0x5566b62531ad in _start (/home/martin/projects/ofono/x86/src/ofonod+0xfc1ad)
0x62100073dd28 is located 40 bytes inside of 4672-byte region [0x62100073dd00,0x62100073ef40)
freed by thread T0 here:
#0 0x7fa0519256c0 in __interceptor_free /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:122
#1 0x5566b64052d7 in g_at_mux_unref ../git/gatchat/gatmux.c:645
#2 0x5566b63d6d19 in close_mux ../git/plugins/quectel.c:199
#3 0x5566b63d7047 in close_serial ../git/plugins/quectel.c:223
#4 0x5566b63db62a in cfun_disable ../git/plugins/quectel.c:1056
#5 0x5566b63f6ae1 in at_chat_finish_command ../git/gatchat/gatchat.c:459
#6 0x5566b63f701b in at_chat_handle_command_response ../git/gatchat/gatchat.c:521
#7 0x5566b63f785b in have_line ../git/gatchat/gatchat.c:600
#8 0x5566b63f87f1 in new_bytes ../git/gatchat/gatchat.c:759
#9 0x5566b640174c in received_data ../git/gatchat/gatio.c:122
#10 0x5566b64047b4 in watch_dispatch ../git/gatchat/gatmux.c:464
#11 0x5566b640313b in dispatch_sources ../git/gatchat/gatmux.c:183
#12 0x5566b64039ea in received_data ../git/gatchat/gatmux.c:268
#13 0x7fa0516d727e in g_main_context_dispatch (/usr/lib/libglib-2.0.so.0+0x6a27e)
previously allocated by thread T0 here:
#0 0x7fa051925ce8 in __interceptor_calloc /build/gcc/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:153
#1 0x5566b6405009 in g_at_mux_new ../git/gatchat/gatmux.c:606
#2 0x5566b6407f6b in g_at_mux_new_gsm0710_basic ../git/gatchat/gatmux.c:1165
#3 0x5566b63da9ba in cmux_cb ../git/plugins/quectel.c:882
#4 0x5566b63f6ae1 in at_chat_finish_command ../git/gatchat/gatchat.c:459
#5 0x5566b63f701b in at_chat_handle_command_response ../git/gatchat/gatchat.c:521
#6 0x5566b63f785b in have_line ../git/gatchat/gatchat.c:600
#7 0x5566b63f87f1 in new_bytes ../git/gatchat/gatchat.c:759
#8 0x5566b640174c in received_data ../git/gatchat/gatio.c:122
#9 0x7fa0516d727e in g_main_context_dispatch (/usr/lib/libglib-2.0.so.0+0x6a27e)
SUMMARY: AddressSanitizer: heap-use-after-free ../git/gatchat/gatmux.c:109 in debug
Shadow bytes around the buggy address:
0x0c42800dfb50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c42800dfb60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c42800dfb70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c42800dfb80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c42800dfb90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c42800dfba0: fd fd fd fd fd[fd]fd fd fd fd fd fd fd fd fd fd
0x0c42800dfbb0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c42800dfbc0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c42800dfbd0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c42800dfbe0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c42800dfbf0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==3640549==ABORTING
It turns out that both L2xx and L4xx modems are a bit
buggy when it comes to send CREG URC's when the tech changes.
Try to overcome this by subscribing to both UREG and CREG,
and poll the other when any of the URC's are received.
Protect from doing simultaneous polls though.
uBlox devices present their USB interfaces well before those interfaces
are ready to respond to any commands. The documentation says to monitor
the 'greeting text' to detect readiness, but this 'greeting text' is not
actually specified for any device other than the TOBY L4.
What seems to work is to probe the device with 'AT' commands until the
device responds, and then to wait an additional second before
proceeding. The TOBY L4 reliably sends its 'greeting text' (+AT: READY)
within this interval.
It would be more rigorous to actually wait for the 'READY' indication
for the TOBY L4, but that would require knowing the device model before
the device model is actually queried. This is doable via the USB
product ID, but overkill when the above heuristic seems to work
reliably.
Before this patch, the ublox plugin was trying to achieve something like
the above with the g_at_chat_set_wakeup_command() function, but that had
some issues:
i) it did not work reliably, in particular failing badly on the TOBY L4
with responses getting out of sync with commands
ii) it was an inappropriate use of the wakeup_command which is intended
for devices that may sleep when there is no communication during some
interval
This patch adds an init sequence that probes the device for readiness
before continuing with initialization.
Just reshuffling the code a bit and the 'disable' path can use the
close_devices() helper to finish up. This also prevents a bug should
the CFUN command fail to disable the modem whereby the 'aux' device
remains open but the 'modem' device has already been closed.
The code for closing all the modem devices and flagging the modem as
unpowered is repeated several times in the driver... this patch puts
this code into a common helper for readability.
Its incorrect to fiddle with the driver attach state when
attaching. When attaching the state is transitioning,
and the correct state will now always be assigned in
the end of the attach process, regardless of result.
Currently there is an issue if the attach state changes and
there are active contexts of which the driver does not
implement the detach_shutdown.
In that case we just release the context (clears CID and
active state), but nothing is signalled on D-Bus or towards
the modem.
Ofono is then out of sync with both the connection manager
and the modem, this manifests itself later on if the modem
changes state of the context, then ofono will not find it
since the CID is cleared, and the connection manager won't
be notified.
In the same way as we consider the driver attached when the
gprs status indicates we are registered, we should consider
it deattached when the status indicates unregistration.
If we don't, then we would not always recover from the case
when deattaching the driver fails. We would just revert back
the driver attached status to true, and "ignore" if the status
indicates the opposite when we check the registration status
afterwards.
The quectel M95 and MC60 modems are picky about when the sim is properly
initialized, so the logic to detect this needs to be in the quectel
plugin.
After doing basic initialization, a CPIN query is issued to detect sim
state.
If the sim is unlocked and ready, a timer is created to wait for the
modem state (AT+QINISTAT) to complete. At this point ofono is notified
about the initialized sim.
If the sim is locked, a listener for "+CPIN: READY" is set up to
know when a pin is entered. Once the indication is received, the timer
is set up as if the sim was unlocked from the beginning.
If the sim is busy/resetting, the CPIN query is issued again.
In case a context get deactivated when a AT+CGACT=0 is
not issued, indicate that to gprs core.
This can happen if the device has an auto activated
LTE context and modem switches over to for instance UTRAN.
According to the manual, when using CMUX on the UC15, it outputs
unsolicited indications on port 2 (i.e. /dev/gsmtty2), so the quectel
plugin must use this when registering for such.
Commit 1fd419e5b4 and
0167c3339c introduced logic that
treated ofono_gprs_cid_activated as an 'attaching' state.
Since gprs_attached_update now guarantees that we
will not get attached without having a context activated
in LTE, this is not needed anymore. It also potentially
interferes in case the driver was actually attaching.
Since we have a different condition for the attach state
when running on LTE, we should consider it in gprs_attached_update.
Previously it's done in some instances. But for instance if
the driver got detached from GPRS but now running on LTE with a
context up, we would be deattached.
If the modem isn't powered on, the use of g_at_chat_set_wakeup_command()
makes ofono send out AT strings in an endless loop. Avoid this by using
g_at_chat_retry() in a timer instead.
The current API doesn't support canceling an in-progress command;
instead g_at_chat_cancel() simply removes the callback.
In cases where the modem doesn't respond at all to a command, a chat is
simply stalled without any way to write new commands to the modem.
Support that case by adding a g_at_chat_retry() function to the API. The
function does nothing if the command is not yet in-progress, or if the
command is finished. Otherwise, it resets the bytes-written counter to
re-write the command string.
There is an issue if an context gets auto activated early,
then provisioning might not have run yet for instance,
so a "new" context is created, which might be duplicated
by a provisioning context later.
So ignore the activated contexts until gprs is ready,
then it calls the driver to list active contexts.
There are cases where the gprs status might updated to for instance
"unknown" while LTE is the bearer.
In that case we should not set the attach state to FALSE,
since then running LTE the conext activation reflects the attached
state.
Previously the valid "unknown" netreg status was set
during startup, but its a bit problematic for gprs.
There might be cases where a LTE context is activated
before netreg is finished updating its status.
Resulting in gprs taking faulty actions.
Instead we set the status to -1 until we are updated
with a known value.
During the time the status is -1, gprs postpones actions until
the status is valid (>= 0).
To detect if a context gets activated before we register
for unsolicited events we need to check if any is
already activated, and flag it auto activated.