Skip to content

Commit 741d166

Browse files
committed
fix(modem): Fixed AT commands to copy strings to prevent overrides
Previously we used std::string_view, which pointed to the lower-layers buffer which might have been reused for other asynchronous operations before processing it, thus causing corruption of replies. Closes #463
1 parent 585e4b3 commit 741d166

File tree

2 files changed

+44
-28
lines changed

2 files changed

+44
-28
lines changed

components/esp_modem/include/cxx_include/esp_modem_command_library_utils.hpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -25,18 +25,16 @@ command_result generic_command(CommandableIf *t, const std::string &command,
2525
* @brief Utility command to send command and return reply (after DCE says OK)
2626
* @param t Anything that is "command-able"
2727
* @param command Command to issue
28-
* @param output String to return
29-
* @param timeout_ms
28+
* @param output String to return (could be either std::string& or std::string_view&)
3029
* @param timeout_ms Command timeout in ms
3130
* @return Generic command return type (OK, FAIL, TIMEOUT)
3231
*/
33-
command_result generic_get_string(CommandableIf *t, const std::string &command, std::string &output, uint32_t timeout_ms = 500);
32+
template <typename T> command_result generic_get_string(CommandableIf *t, const std::string &command, T &output, uint32_t timeout_ms = 500);
3433

3534
/**
3635
* @brief Generic command that passes on "OK" and fails on "ERROR"
3736
* @param t Anything that is "command-able"
3837
* @param command Command to issue
39-
* @param timeout
4038
* @param timeout_ms Command timeout in ms
4139
* @return Generic command return type (OK, FAIL, TIMEOUT)
4240
*/

components/esp_modem/src/esp_modem_command_library.cpp

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,35 @@ command_result generic_command(CommandableIf *t, const std::string &command,
5151
return generic_command(t, command, pass, fail, timeout_ms);
5252
}
5353

54-
static command_result generic_get_string(CommandableIf *t, const std::string &command, std::string_view &output, uint32_t timeout_ms = 500)
54+
/*
55+
* Purpose of this namespace is to provide different means of assigning the result to a string-like parameter.
56+
* By default we assign strings, which comes with an allocation. Alternatively we could take `std::span`
57+
* with user's buffer and directly copy the result, thus avoiding allocations (this is not used as of now)
58+
*/
59+
namespace str_copy {
60+
61+
bool set(std::string &dest, std::string_view &src)
62+
{
63+
dest = src;
64+
return true;
65+
}
66+
67+
/* This is an example of using std::span output in generic_get_string()
68+
bool set(std::span<char> &dest, std::string_view &src)
69+
{
70+
if (dest.size() >= src.size()) {
71+
std::copy(src.begin(), src.end(), dest.data());
72+
dest = dest.subspan(0, src.size());
73+
return true;
74+
}
75+
ESP_LOGE(TAG, "Cannot set result of size %d (to span of size %d)", dest.size(), src.size());
76+
return false;
77+
}
78+
*/
79+
80+
} // str_copy
81+
82+
template <typename T> command_result generic_get_string(CommandableIf *t, const std::string &command, T &output, uint32_t timeout_ms)
5583
{
5684
ESP_LOGV(TAG, "%s", __func__ );
5785
return t->command(command, [&](uint8_t *data, size_t len) {
@@ -70,26 +98,16 @@ static command_result generic_get_string(CommandableIf *t, const std::string &co
7098
} else if (token.find("ERROR") != std::string::npos) {
7199
return command_result::FAIL;
72100
} else if (token.size() > 2) {
73-
output = token;
101+
if (!str_copy::set(output, token)) {
102+
return command_result::FAIL;
103+
}
74104
}
75105
response = response.substr(pos + 1);
76106
}
77107
return command_result::TIMEOUT;
78108
}, timeout_ms);
79109
}
80110

81-
command_result generic_get_string(CommandableIf *t, const std::string &command, std::string &output, uint32_t timeout_ms)
82-
{
83-
ESP_LOGV(TAG, "%s", __func__ );
84-
std::string_view out;
85-
auto ret = generic_get_string(t, command, out, timeout_ms);
86-
if (ret == command_result::OK) {
87-
output = out;
88-
}
89-
return ret;
90-
}
91-
92-
93111
command_result generic_command_common(CommandableIf *t, const std::string &command, uint32_t timeout_ms)
94112
{
95113
ESP_LOGV(TAG, "%s", __func__ );
@@ -153,7 +171,7 @@ command_result hang_up(CommandableIf *t)
153171
command_result get_battery_status(CommandableIf *t, int &voltage, int &bcs, int &bcl)
154172
{
155173
ESP_LOGV(TAG, "%s", __func__ );
156-
std::string_view out;
174+
std::string out;
157175
auto ret = generic_get_string(t, "AT+CBC\r", out);
158176
if (ret != command_result::OK) {
159177
return ret;
@@ -189,7 +207,7 @@ command_result get_battery_status(CommandableIf *t, int &voltage, int &bcs, int
189207
command_result get_battery_status_sim7xxx(CommandableIf *t, int &voltage, int &bcs, int &bcl)
190208
{
191209
ESP_LOGV(TAG, "%s", __func__ );
192-
std::string_view out;
210+
std::string out;
193211
auto ret = generic_get_string(t, "AT+CBC\r", out);
194212
if (ret != command_result::OK) {
195213
return ret;
@@ -224,7 +242,7 @@ command_result set_flow_control(CommandableIf *t, int dce_flow, int dte_flow)
224242
command_result get_operator_name(CommandableIf *t, std::string &operator_name, int &act)
225243
{
226244
ESP_LOGV(TAG, "%s", __func__ );
227-
std::string_view out;
245+
std::string out;
228246
auto ret = generic_get_string(t, "AT+COPS?\r", out, 75000);
229247
if (ret != command_result::OK) {
230248
return ret;
@@ -361,7 +379,7 @@ command_result set_cmux(CommandableIf *t)
361379
command_result read_pin(CommandableIf *t, bool &pin_ok)
362380
{
363381
ESP_LOGV(TAG, "%s", __func__ );
364-
std::string_view out;
382+
std::string out;
365383
auto ret = generic_get_string(t, "AT+CPIN?\r", out);
366384
if (ret != command_result::OK) {
367385
return ret;
@@ -413,7 +431,7 @@ command_result at_raw(CommandableIf *t, const std::string &cmd, std::string &out
413431
command_result get_signal_quality(CommandableIf *t, int &rssi, int &ber)
414432
{
415433
ESP_LOGV(TAG, "%s", __func__ );
416-
std::string_view out;
434+
std::string out;
417435
auto ret = generic_get_string(t, "AT+CSQ\r", out);
418436
if (ret != command_result::OK) {
419437
return ret;
@@ -451,7 +469,7 @@ command_result set_network_attachment_state(CommandableIf *t, int state)
451469
command_result get_network_attachment_state(CommandableIf *t, int &state)
452470
{
453471
ESP_LOGV(TAG, "%s", __func__ );
454-
std::string_view out;
472+
std::string out;
455473
auto ret = generic_get_string(t, "AT+CGATT?\r", out);
456474
if (ret != command_result::OK) {
457475
return ret;
@@ -478,7 +496,7 @@ command_result set_radio_state(CommandableIf *t, int state)
478496
command_result get_radio_state(CommandableIf *t, int &state)
479497
{
480498
ESP_LOGV(TAG, "%s", __func__ );
481-
std::string_view out;
499+
std::string out;
482500
auto ret = generic_get_string(t, "AT+CFUN?\r", out);
483501
if (ret != command_result::OK) {
484502
return ret;
@@ -543,7 +561,7 @@ command_result set_network_bands_sim76xx(CommandableIf *t, const std::string &mo
543561
command_result get_network_system_mode(CommandableIf *t, int &mode)
544562
{
545563
ESP_LOGV(TAG, "%s", __func__ );
546-
std::string_view out;
564+
std::string out;
547565
auto ret = generic_get_string(t, "AT+CNSMOD?\r", out);
548566
if (ret != command_result::OK) {
549567
return ret;
@@ -571,7 +589,7 @@ command_result set_gnss_power_mode(CommandableIf *t, int mode)
571589
command_result get_gnss_power_mode(CommandableIf *t, int &mode)
572590
{
573591
ESP_LOGV(TAG, "%s", __func__ );
574-
std::string_view out;
592+
std::string out;
575593
auto ret = generic_get_string(t, "AT+CGNSPWR?\r", out);
576594
if (ret != command_result::OK) {
577595
return ret;

0 commit comments

Comments
 (0)