Fix streaming bug affecting mostly the pm5

Bug triggered with:
lf read -s 50000
hw ping

ReadLF_realtime() in lfsampling.c never knew how many samples the client wanted — for the realtime path, lf_sample_payload_t.samples was never populated on the client side, and the ARM handler in appmain.c (CMD_LF_ACQ_RAW_ADC/CMD_LF_SNIFF_RAW_ADC) ignored it anyway. So the device just streamed samples indefinitely, only checking for a stop request roughly once per 64-sample USB burst, and even after noticing it, flushed one more trailing partial burst via async_usb_write_stop().

Meanwhile the client (WaitForRawDataTimeout in comms.c) stops listening in raw mode the instant it has collected exactly the number of bytes it asked for (e.g. 50000), then switches back to normal framed-packet parsing. Since the device kept sending more bytes past that point, those extra bytes sat unread in the pipe and got misinterpreted as a PacketResponseNGPreamble/OLD-frame header, corrupting every subsequent USB exchange (hw ping, etc.) — reproducible even with a plain lf read -s 50000, no trigger/COTAG needed.

Moreover, we make sure the client doesn't stop listening, even briefly, during stream reception.
This commit is contained in:
Philippe Teuwen
2026-08-26 20:16:46 +02:00
parent 727a7c879f
commit 7fb8927523
6 changed files with 37 additions and 18 deletions
+2 -2
View File
@@ -1737,7 +1737,7 @@ static void PacketReceived(PacketCommandNG *packet) {
case CMD_LF_ACQ_RAW_ADC: {
lf_sample_payload_t *payload = (lf_sample_payload_t *)packet->data.asBytes;
if (payload->realtime) {
ReadLF_realtime(true, payload->cotag);
ReadLF_realtime(true, payload->cotag, payload->samples);
} else {
uint32_t bits = SampleLF(payload->verbose, payload->samples, true, payload->cotag);
reply_ng(CMD_LF_ACQ_RAW_ADC, PM3_SUCCESS, (uint8_t *)&bits, sizeof(bits));
@@ -1766,7 +1766,7 @@ static void PacketReceived(PacketCommandNG *packet) {
case CMD_LF_SNIFF_RAW_ADC: {
lf_sample_payload_t *payload = (lf_sample_payload_t *)packet->data.asBytes;
if (payload->realtime) {
ReadLF_realtime(false, false);
ReadLF_realtime(false, false, payload->samples);
} else {
uint32_t bits = SniffLF(payload->verbose, payload->samples, true);
reply_ng(CMD_LF_SNIFF_RAW_ADC, PM3_SUCCESS, (uint8_t *)&bits, sizeof(bits));
+8 -1
View File
@@ -455,7 +455,7 @@ uint32_t SampleLF(bool verbose, uint32_t sample_size, bool ledcontrol, bool cota
* @param reader_field - true for reading tags, false for sniffing
* @return sampling result
**/
int ReadLF_realtime(bool reader_field, bool cotag) {
int ReadLF_realtime(bool reader_field, bool cotag, uint32_t sample_limit) {
// parameters from config and constants
const uint8_t bits_per_sample = config.bits_per_sample;
const int16_t trigger_threshold = config.trigger_threshold;
@@ -469,6 +469,8 @@ int ReadLF_realtime(bool reader_field, bool cotag) {
uint8_t last_byte = 0;
uint8_t curr_byte = 0;
int return_value = PM3_SUCCESS;
// Total samples streamed so far
uint32_t total_logged = 0;
uint32_t usb_buffer_len = 0, sample_buffer_len;
usb_get_ep_size(NULL, &usb_buffer_len, NULL);
@@ -536,6 +538,7 @@ int ReadLF_realtime(bool reader_field, bool cotag) {
curr_byte = data.numbits >> 3;
if (curr_byte > last_byte) {
async_usb_write_pushByte(data.buffer[last_byte]);
total_logged++;
}
last_byte = curr_byte;
@@ -558,6 +561,10 @@ int ReadLF_realtime(bool reader_field, bool cotag) {
break;
}
}
if (sample_limit > 0 && total_logged >= sample_limit) {
break;
}
}
}
+2 -1
View File
@@ -57,9 +57,10 @@ uint32_t SampleLF(bool verbose, uint32_t sample_size, bool ledcontrol, bool cota
* Uses parameters in config. Only bits_per_sample = 8 is working now
*
* @param reader_field - true for reading tags, false for sniffing
* @param sample_limit - stop once this many samples have been streamed, 0 = unlimited
* @return sampling result
**/
int ReadLF_realtime(bool reader_field, bool cotag);
int ReadLF_realtime(bool reader_field, bool cotag, uint32_t sample_limit);
/**
* Initializes the FPGA for sniff-mode (field off), and acquires the samples.
+14 -10
View File
@@ -787,6 +787,7 @@ static int lf_read_internal(bool realtime, bool verbose, uint64_t samples, bool
payload.realtime = realtime;
payload.verbose = verbose;
payload.cotag = cotag;
payload.samples = (samples > MAX_LF_SAMPLES) ? MAX_LF_SAMPLES : samples;
sample_config current_config;
int retval = lf_getconfig(&current_config);
@@ -820,12 +821,14 @@ static int lf_read_internal(bool realtime, bool verbose, uint64_t samples, bool
SendCommandNG(CMD_LF_ACQ_RAW_ADC, (uint8_t *)&payload, sizeof(payload));
if (is_trigger_threshold_set) {
size_t first_receive_len = 32;
// Wait until a bunch of data arrives
first_receive_len = WaitForRawDataTimeout(realtimeBuf, first_receive_len, -1, false);
sample_bytes = WaitForRawDataTimeout(realtimeBuf + first_receive_len, sample_bytes - first_receive_len, 1000, true);
// Wait until a bunch of data arrives. Keep raw mode on across both
// calls so the comm thread doesn't briefly fall back to parsing
// framed packets out of the still-arriving sample stream.
first_receive_len = WaitForRawDataTimeout(realtimeBuf, first_receive_len, -1, false, true);
sample_bytes = WaitForRawDataTimeout(realtimeBuf + first_receive_len, sample_bytes - first_receive_len, 1000, true, false);
sample_bytes += first_receive_len;
} else {
sample_bytes = WaitForRawDataTimeout(realtimeBuf, sample_bytes, 1000, true);
sample_bytes = WaitForRawDataTimeout(realtimeBuf, sample_bytes, 1000, true, false);
}
samples = sample_bytes * 8 / bits_per_sample;
PrintAndLogEx(INFO, "Done: %" PRIu64 " samples (%zu bytes)", samples, sample_bytes);
@@ -835,7 +838,6 @@ static int lf_read_internal(bool realtime, bool verbose, uint64_t samples, bool
free(realtimeBuf);
} else {
payload.samples = (samples > MAX_LF_SAMPLES) ? MAX_LF_SAMPLES : samples;
SendCommandNG(CMD_LF_ACQ_RAW_ADC, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
@@ -920,6 +922,7 @@ int lf_sniff(bool realtime, bool verbose, uint64_t samples) {
lf_sample_payload_t payload = {0};
payload.realtime = realtime;
payload.verbose = verbose;
payload.samples = (samples > MAX_LF_SAMPLES) ? MAX_LF_SAMPLES : samples;
sample_config current_config;
int retval = lf_getconfig(&current_config);
@@ -953,12 +956,14 @@ int lf_sniff(bool realtime, bool verbose, uint64_t samples) {
SendCommandNG(CMD_LF_SNIFF_RAW_ADC, (uint8_t *)&payload, sizeof(payload));
if (is_trigger_threshold_set) {
size_t first_receive_len = 32;
// Wait until a bunch of data arrives
first_receive_len = WaitForRawDataTimeout(realtimeBuf, first_receive_len, -1, false);
sample_bytes = WaitForRawDataTimeout(realtimeBuf + first_receive_len, sample_bytes - first_receive_len, 1000, true);
// Wait until a bunch of data arrives. Keep raw mode on across both
// calls so the comm thread doesn't briefly fall back to parsing
// framed packets out of the still-arriving sample stream.
first_receive_len = WaitForRawDataTimeout(realtimeBuf, first_receive_len, -1, false, true);
sample_bytes = WaitForRawDataTimeout(realtimeBuf + first_receive_len, sample_bytes - first_receive_len, 1000, true, false);
sample_bytes += first_receive_len;
} else {
sample_bytes = WaitForRawDataTimeout(realtimeBuf, sample_bytes, 1000, true);
sample_bytes = WaitForRawDataTimeout(realtimeBuf, sample_bytes, 1000, true, false);
}
samples = sample_bytes * 8 / bits_per_sample;
PrintAndLogEx(INFO, "Done: %" PRIu64 " samples (%zu bytes)", samples, sample_bytes);
@@ -968,7 +973,6 @@ int lf_sniff(bool realtime, bool verbose, uint64_t samples) {
free(realtimeBuf);
} else {
payload.samples = (samples > MAX_LF_SAMPLES) ? MAX_LF_SAMPLES : samples;
SendCommandNG(CMD_LF_SNIFF_RAW_ADC, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
if (is_trigger_threshold_set) {
+10 -3
View File
@@ -1010,7 +1010,7 @@ static size_t communication_delay(void) {
* @param show_process print how many bytes are received
* @return the number of received bytes
*/
size_t WaitForRawDataTimeout(uint8_t *buffer, size_t len, size_t ms_timeout, bool show_process) {
size_t WaitForRawDataTimeout(uint8_t *buffer, size_t len, size_t ms_timeout, bool show_process, bool keep_raw_mode) {
uint8_t print_counter = 0;
size_t last_pos = 0;
@@ -1058,7 +1058,7 @@ size_t WaitForRawDataTimeout(uint8_t *buffer, size_t len, size_t ms_timeout, boo
last_pos = pos;
msleep(10);
}
if (pos == len && (ms_timeout != (size_t) - 1)) {
if (pos == len && (ms_timeout != (size_t) - 1) && (keep_raw_mode == false)) {
// If ms_timeout != -1, when the desired data is received, tell the arm side
// to stop the current process, and wait for some time to make sure the process
// has been stopped.
@@ -1067,7 +1067,14 @@ size_t WaitForRawDataTimeout(uint8_t *buffer, size_t len, size_t ms_timeout, boo
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
msleep(ms_timeout);
}
SetCommunicationReceiveMode(false);
// Caller is about to chain another raw read: keep raw-receive mode
// on so the comm thread never drops back into normal framed-packet
// parsing in between the two reads. Doing so - even briefly - lets it
// misparse in-flight sample bytes as a PacketResponseNG preamble and
// corrupts every USB exchange that follows.
if (keep_raw_mode == false) {
SetCommunicationReceiveMode(false);
}
pos = __atomic_load_n(&comm_raw_pos, __ATOMIC_SEQ_CST);
return pos;
}
+1 -1
View File
@@ -110,7 +110,7 @@ int TestProxmark(pm3_device_t *dev);
void CloseProxmark(pm3_device_t *dev);
void StartReconnectProxmark(void);
size_t WaitForRawDataTimeout(uint8_t *buffer, size_t len, size_t ms_timeout, bool show_process);
size_t WaitForRawDataTimeout(uint8_t *buffer, size_t len, size_t ms_timeout, bool show_process, bool keep_raw_mode);
bool WaitForResponseTimeoutW(uint32_t cmd, PacketResponseNG *response, size_t ms_timeout, bool show_warning);
bool WaitForResponseTimeout(uint32_t cmd, PacketResponseNG *response, size_t ms_timeout);
bool WaitForResponse(uint32_t cmd, PacketResponseNG *response);