If the fixed sized string contains no characters or is shorter than the explicit
size, NUL bytes will be included. If we try to append exactly the size of the
string, we'll end up with embedded NULs in our string to print, so the actual
output will be cut, even if the string is longer after the embedded NUL bytes.
E.g.:
>>>>>> TLV:
>>>>>> type = "GERAN Info" (0x10)
>>>>>> length = 61
>>>>>> value = 00:00:00:00:00:00:00:00:00:00:00:00:FF:FF:FF:FF:28:00:03:7D:6F:00:00:32:F4:51:B3:00:4D:00:11:2A:00:8A:3C:00:00:32:F4:51:B3:00:63:00:30:14:00:89:3C:00:00:32:F4:51:B3:00:59:00:11:0D:00
>>>>>> translated = [ cell_id = '0' plmn = '
With this fix, we avoid this by explicitly finishing ourselves the fixed sized
string with a NUL byte, and then adding the C string as the non-fixed sized
ones, i.e. until the first NUL byte is found.
E.g.:
>>>>>> TLV:
>>>>>> type = "GERAN Info" (0x10)
>>>>>> length = 61
>>>>>> value = 00:00:00:00:00:00:00:00:00:00:00:00:FF:FF:FF:FF:28:00:03:7D:6F:00:00:32:F4:51:B3:00:4D:00:11:2A:00:8A:3C:00:00:32:F4:51:B3:00:63:00:30:14:00:89:3C:00:00:32:F4:51:B3:00:59:00:11:0D:00
>>>>>> translated = [ cell_id = '0' plmn = '' lac = '0' geran_absolute_rf_channel_number = '0' base_station_identity_code = '0' timing_advance = '4294967295' rx_level = '40' cell = '{ [0] = '[ cell_id = '28541' plmn = '2\xf4Q' lac = '179' geran_absolute_rf_channel_number = '77' base_station_identity_code = '17' rx_level = '42' ] ' [1] = '[ cell_id = '15498' plmn = '2\xf4Q' lac = '179' geran_absolute_rf_channel_number = '99' base_station_identity_code = '48' rx_level = '20' ] ' [2] = '[ cell_id = '15497' plmn = '2\xf4Q' lac = '179' geran_absolute_rf_channel_number = '89' base_station_identity_code = '17' rx_level = '13' ] '}' ]
This change triggers an API break.
When building structs to be included in the public header, we were just relying
on using the 'public_format' of each variable. This is an error, as the variable
may be more complex than just public/private. E.g. could be another struct, or
an array, or a fixed sized string, as in the example.
In particular, this bug currently affects one public type, where one of its
elements changes from being just a pointer to a string to a fixed sized array of
4 bytes.
The following type is changed from:
typedef struct _QmiMessageNasGetCellLocationInfoOutputGeranInfoCellElement {
guint32 cell_id;
gchar * plmn;
guint16 lac;
guint16 geran_absolute_rf_channel_number;
guint8 base_station_identity_code;
guint16 rx_level;
} QmiMessageNasGetCellLocationInfoOutputGeranInfoCellElement;
To:
typedef struct _QmiMessageNasGetCellLocationInfoOutputGeranInfoCellElement {
guint32 cell_id;
gchar plmn[4];
guint16 lac;
guint16 geran_absolute_rf_channel_number;
guint8 base_station_identity_code;
guint16 rx_level;
} QmiMessageNasGetCellLocationInfoOutputGeranInfoCellElement;
Thanks to Joseba Sanjuan <joseba.sanjuan@gmail.com> for finding the bug.
Which currently fails:
{*LOG(error):{Qmi-FATAL-CRITICAL: qmi_message_tlv_read_fixed_size_string: assertion 'out != NULL' failed}:LOG*}
(/home/aleksander/Development/foss/libqmi/src/libqmi-glib/test/.libs/test-generated:12179): Qmi-CRITICAL **: qmi_message_tlv_read_fixed_size_string: assertion 'out != NULL' failed
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00007ffff717eae0 in g_logv () from /usr/lib/libglib-2.0.so.0
(gdb) bt
#0 0x00007ffff717eae0 in g_logv () from /usr/lib/libglib-2.0.so.0
#1 0x00007ffff717ed1f in g_log () from /usr/lib/libglib-2.0.so.0
#2 0x00007ffff7a5381e in qmi_message_tlv_read_fixed_size_string (self=0x7fffe8007d30, tlv_offset=20, offset=0x7fffffffdcc8, string_length=3, out=0x0, error=0x0) at qmi-message.c:1817
#3 0x00007ffff7acce60 in __qmi_message_nas_get_cell_location_info_response_parse (message=0x7fffe8007d30, error=0x7fffffffded8) at qmi-nas.c:26772
#4 0x00007ffff7af46fa in get_cell_location_info_ready (device=0x64e8b0, res=0x65d990, simple=0x65d920) at qmi-nas.c:47982
#5 0x00007ffff76ff6d7 in g_simple_async_result_complete () from /usr/lib/libgio-2.0.so.0
#6 0x00007ffff76ff739 in ?? () from /usr/lib/libgio-2.0.so.0
#7 0x00007ffff717791d in g_main_context_dispatch () from /usr/lib/libglib-2.0.so.0
#8 0x00007ffff7177cf8 in ?? () from /usr/lib/libglib-2.0.so.0
#9 0x00007ffff7178022 in g_main_loop_run () from /usr/lib/libglib-2.0.so.0
#10 0x0000000000403e9b in test_fixture_loop_run (fixture=0x63c800) at test-fixture.c:321
#11 0x0000000000406da4 in test_generated_nas_get_cell_location_info (fixture=0x63c800) at test-generated.c:562
#12 0x00007ffff719d0f3 in ?? () from /usr/lib/libglib-2.0.so.0
#13 0x00007ffff719d2c2 in ?? () from /usr/lib/libglib-2.0.so.0
#14 0x00007ffff719d2c2 in ?? () from /usr/lib/libglib-2.0.so.0
#15 0x00007ffff719d2c2 in ?? () from /usr/lib/libglib-2.0.so.0
#16 0x00007ffff719d62b in g_test_run_suite () from /usr/lib/libglib-2.0.so.0
#17 0x00007ffff719d661 in g_test_run () from /usr/lib/libglib-2.0.so.0
#18 0x0000000000406ef0 in main (argc=1, argv=0x7fffffffe678) at test-generated.c:584
(gdb) fr 3
#3 0x00007ffff7acce60 in __qmi_message_nas_get_cell_location_info_response_parse (message=0x7fffe8007d30, error=0x7fffffffded8) at qmi-nas.c:26772
warning: Source file is more recent than executable.
26772 if (!qmi_message_tlv_read_fixed_size_string (message, init_offset, &offset, 3, &cell_aux.plmn[0], NULL))
(gdb) p cell_aux
$1 = {cell_id = 28541, plmn = 0x0, lac = 0, geran_absolute_rf_channel_number = 0, base_station_identity_code = 0 '\000', rx_level = 0}
In the optional TLV case, 'tlv_error' was never set to TRUE
when the optional TLV was missing, because the return value of
qmi_message_tlv_read_init() was ignored. This caused
"self->arg_*_set" to always be TRUE and NULL strings to be
returned to callers requesting the TLV value later.
Also prevent incorrect "Left X bytes unread when getting..."
messages caused when the TLV is missing.
This bug was found when probing a modem that does not return
an MEID TLV to DMSGetIds (because it is GSM/UMTS/LTE only),
but qmi_message_dms_get_ids_output_get_meid() returned TRUE
and a NULL 'str':
if (qmi_message_dms_get_ids_output_get_meid (output, &str, NULL) &&
--> str[0] != '\0' && str[0] != '0') {
Bug introduced in b143b7f6 (qmi-codegen: use the new TLV reader API).
Before:
=======
gsize offset = 0;
gsize init_offset;
gboolean tlv_error = FALSE;
init_offset = qmi_message_tlv_read_init (message, QMI_INDICATION_DMS_EVENT_REPORT_OUTPUT_TLV_POWER_STATE, NULL, NULL);
<<<snip>>>
/* The remaining size of the buffer needs to be 0 if we successfully read the TLV */
if ((offset = __qmi_message_tlv_read_remaining_size (message, init_offset, offset)) > 0) {
g_warning ("Left '%" G_GSIZE_FORMAT "' bytes unread when getting the 'Power State' TLV", offset);
}
qmi_indication_dms_event_report_output_power_state_out:
if (!tlv_error)
self->arg_power_state_set = TRUE;
After:
======
gsize offset = 0;
gsize init_offset;
if ((init_offset = qmi_message_tlv_read_init (message, QMI_INDICATION_DMS_EVENT_REPORT_OUTPUT_TLV_POWER_STATE, NULL, NULL)) == 0) {
goto qmi_indication_dms_event_report_output_power_state_out;
}
<<<snip>>>
/* The remaining size of the buffer needs to be 0 if we successfully read the TLV */
if ((offset = __qmi_message_tlv_read_remaining_size (message, init_offset, offset)) > 0) {
g_warning ("Left '%" G_GSIZE_FORMAT "' bytes unread when getting the 'Power State' TLV", offset);
}
self->arg_power_state_set = TRUE;
qmi_indication_dms_event_report_output_power_state_out:
;
Allows to check whether the user is allowed to use the QMI device.
Also fixes qmi_proxy_open() to make sure we always set the GError when FALSE is
returned.
Currently, the QMI proxy process assumes that it is run as root user and
that all incoming client connection users are also root.
However, it's not always preferable to run the QMI proxy as root for
security reasons. On some platforms, the QMI proxy could be constrained
to run as a less-privileged user and specially granted the permission to
access the QMI device. So, adding a compile time flag in libqmi to check
for the specified user, rather than assume it to be the root user. If the flag
is not sent, it'll revert to the existing behaviour of checking for
user=root(i.e UID=0)
The standard timeout/idle methods will attach the sources to the default
context, not the thread default context. So, create the GSources ourselves
and attach them to the thread default context.
If NULL is specified in g_source_attach() it will attach to the default context,
not to the thread-default one if one was given with
g_main_context_push_thread_default().
This caused that QmiDevices started in the non-main thread would still attach
their socket listening sources to the GMainContext in the main thread.
If set to TRUE, this property will make the QmiDevice not check for the
existence and file type of the given GFile.
This is a construct write-only property, and should be only used when
building the QmiDevice with g_async_initable_new_async() explicitly (i.e. no
library helper method given).
The transaction ID in non-CTL messages wasn't being written properly when
qmi_message_set_transaction_id() was used. Internally this never happened, but
this method is part of the API, so could be a big issue for users.
This patch fixes a segfault caused by a race condition where an incoming
request has just been accepted and allocated, but a device for it has yet
to be opened, and an old request is closed. The checks prevent a strcmp
against unexisting devices.
https://bugs.freedesktop.org/show_bug.cgi?id=77335