Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package usbredir for openSUSE:Factory checked in at 2021-12-22 20:17:47 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/usbredir (Old) and /work/SRC/openSUSE:Factory/.usbredir.new.2520 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "usbredir" Wed Dec 22 20:17:47 2021 rev:16 rq:941785 version:0.12.0 Changes: -------- --- /work/SRC/openSUSE:Factory/usbredir/usbredir.changes 2021-08-26 23:14:38.232258872 +0200 +++ /work/SRC/openSUSE:Factory/.usbredir.new.2520/usbredir.changes 2021-12-22 20:18:23.967856411 +0100 @@ -1,0 +2,12 @@ +Mon Dec 20 23:10:25 UTC 2021 - Dirk M??ller <dmuel...@suse.com> + +- update to 0.12.0: + * Implement dropping packets from isochronous devices + when buffer is owned by usbredirparser library + * Use packet size limit on deserialization + * Fix possible bad state in deserialization logic + * Fix possible memory leak in serialization logic + * Fix (un)serialization with empty write buffers + * Improvements to usbredirparserfuzz + +------------------------------------------------------------------- Old: ---- usbredir-0.11.0.tar.xz usbredir-0.11.0.tar.xz.sig New: ---- usbredir-0.12.0.tar.xz usbredir-0.12.0.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ usbredir.spec ++++++ --- /var/tmp/diff_new_pack.XTD9Mv/_old 2021-12-22 20:18:24.519856670 +0100 +++ /var/tmp/diff_new_pack.XTD9Mv/_new 2021-12-22 20:18:24.527856673 +0100 @@ -18,7 +18,7 @@ Name: usbredir -Version: 0.11.0 +Version: 0.12.0 Release: 0 Summary: A protocol for redirecting USB traffic License: GPL-2.0-or-later AND LGPL-2.1-or-later ++++++ usbredir-0.11.0.tar.xz -> usbredir-0.12.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/ChangeLog.md new/usbredir-0.12.0/ChangeLog.md --- old/usbredir-0.11.0/ChangeLog.md 2021-08-10 08:13:51.746648000 +0200 +++ new/usbredir-0.12.0/ChangeLog.md 2021-11-12 11:45:49.992210900 +0100 @@ -1,3 +1,13 @@ +# usbredir-0.12.0 - 12 Nov 2021 + +- !47 Implement dropping packets from isochronous devices + when buffer is owned by usbredirparser library +- !50 Use packet size limit on deserialization +- !54 Fix possible bad state in deserialization logic +- !48 Fix possible memory leak in serialization logic +- !45 Fix (un)serialization with empty write buffers +- !42 !46 !52 Improvements to usbredirparserfuzz + # usbredir-0.11.0 - 10 Aug 2021 - !40 Fixes use-after-free on usbredirparser serialization diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/build-aux/oss-fuzz.sh new/usbredir-0.12.0/build-aux/oss-fuzz.sh --- old/usbredir-0.11.0/build-aux/oss-fuzz.sh 2021-08-10 08:13:51.746648000 +0200 +++ new/usbredir-0.12.0/build-aux/oss-fuzz.sh 2021-11-12 11:45:49.994210700 +0100 @@ -46,6 +46,9 @@ # Don't use "-Wl,--no-undefined" -Db_lundef=false + + # Enable internal tests + -Dextra-checks=true ) if ! meson setup "${config[@]}" -- "$builddir"; then diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/fuzzing/usbredirparserfuzz.cc new/usbredir-0.12.0/fuzzing/usbredirparserfuzz.cc --- old/usbredir-0.11.0/fuzzing/usbredirparserfuzz.cc 2021-08-10 08:13:51.747648000 +0200 +++ new/usbredir-0.12.0/fuzzing/usbredirparserfuzz.cc 2021-11-12 11:45:50.003210800 +0100 @@ -19,9 +19,10 @@ #include <array> #include <algorithm> #include <memory> +#include <type_traits> +#include <cassert> #include <cinttypes> -#include <cstdio> #include <cstring> #include <limits> @@ -43,37 +44,49 @@ std::unique_ptr<struct usbredirparser, ParserDeleter> parser; std::unique_ptr<FuzzedDataProvider> fdp; -void log(const char *format, ...) +void parser_log(void *priv, int level, const char *msg) { -#if 0 - va_list args; - - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); -#endif } -void parser_log(void *priv, int level, const char *msg) +int wobbly_read_write_count(int count) { - log("[%d] %s\n", level, msg); + if (count > (1024 * 1024)) { + return count; + } + + return std::min(count, fdp->ConsumeIntegralInRange(1, 4 * count)); } int parser_read(void *priv, uint8_t *data, int count) { - log("%s: %d bytes\n", __func__, count); + // Simulate short reads + return fdp->ConsumeData(data, wobbly_read_write_count(count)); +} - return fdp->ConsumeData(data, count); +// Read over complete input buffer to detect buffer overflows +void read_all(const void *data, size_t count) +{ +#ifdef __cpp_lib_smart_ptr_for_overwrite + const auto buf = std::make_unique_for_overwrite<uint8_t[]>(count); +#else + const auto buf = std::make_unique<uint8_t[]>(count); +#endif + + memcpy(buf.get(), data, count); +} + +template <typename T, typename = std::enable_if_t<std::is_class<T>::value>> +void read_all(const T *ptr) +{ + read_all(ptr, sizeof(T)); } int parser_write(void *priv, uint8_t *data, int count) { - log("%s: %d bytes\n", __func__, count); + // Simulate short writes + count = wobbly_read_write_count(count); - // Read over complete source buffer to detect buffer overflows on write - void *buf = malloc(count); - memcpy(buf, data, count); - free(buf); + read_all(data, count); return count; } @@ -81,60 +94,24 @@ void parser_device_connect(void *priv, struct usb_redir_device_connect_header *device_connect) { - log("%s: speed=%d, class=%d, subclass=%d, protocol=%d, vendor=%04x," - " product=%04x\n", - __func__, - device_connect->speed, - device_connect->device_class, - device_connect->device_subclass, - device_connect->device_protocol, - device_connect->vendor_id, - device_connect->product_id); } void parser_device_disconnect(void *priv) { - log("%s\n", __func__); } void parser_reset(void *priv) { - log("%s\n", __func__); } void parser_interface_info(void *priv, struct usb_redir_interface_info_header *info) { - uint32_t i; - - log("%s:", __func__); - - for (i = 0; i < info->interface_count; i++) { - log(" [interface %d, class %d, subclass %d, protocol %d]", - info->interface[i], info->interface_class[i], - info->interface_subclass[i], info->interface_protocol[i]); - } - - log("\n"); } void parser_ep_info(void *priv, struct usb_redir_ep_info_header *ep_info) { - int i; - - log("%s:", __func__); - - for (i = 0; i < 32; i++) { - if (ep_info->type[i] != usb_redir_type_invalid) { - log(" [index %d, type %d, interval %d, interface %d," - " max-packetsize %d]", - i, (int)ep_info->type[i], (int)ep_info->interval[i], - (int)ep_info->interface[i], ep_info->max_packet_size[i]); - } - } - - log("\n"); } void parser_set_configuration(void *priv, uint64_t id, @@ -149,8 +126,6 @@ void parser_configuration_status(void *priv, uint64_t id, struct usb_redir_configuration_status_header *config_status) { - log("%s: id=%" PRIu64 ", status=%d, configuration=%d\n", - __func__, id, config_status->status, config_status->configuration); } void parser_set_alt_setting(void *priv, uint64_t id, @@ -166,11 +141,6 @@ void parser_alt_setting_status(void *priv, uint64_t id, struct usb_redir_alt_setting_status_header *alt_setting_status) { - log("%s: id=%" PRIu64 ", status=%d, interface=%d, alt=%d\n", - __func__, id, - alt_setting_status->status, - alt_setting_status->interface, - alt_setting_status->alt); } void parser_start_iso_stream(void *priv, uint64_t id, @@ -232,36 +202,12 @@ usbredirfilter_free(rules); } -void dump_data(const uint8_t *data, const int len) -{ - int i; - - if (len == 0) { - return; - } - - log(" "); - for (i = 0; i < len; i++) { - log(" %02X", (unsigned int)data[i]); - } - log("\n"); -} - void parser_control_packet(void *priv, uint64_t id, struct usb_redir_control_packet_header *control_packet, uint8_t *data, int data_len) { - log("%s: id=%" PRIu64 ", endpoint=%d, request=%d, requesttype=%d," - " status=%d, value=%d, index=%d, length=%d\n", - __func__, id, - control_packet->endpoint, - control_packet->request, - control_packet->requesttype, - control_packet->status, - control_packet->value, - control_packet->index, - control_packet->length); - dump_data(data, data_len); + read_all(control_packet); + read_all(data, data_len); usbredirparser_free_packet_data(parser.get(), data); } @@ -269,15 +215,8 @@ struct usb_redir_bulk_packet_header *bulk_packet, uint8_t *data, int data_len) { - log("%s: id=%" PRIu64 ", endpoint=%d, status=%d, length=%d, stream_id=%d," - " length_high=%d\n", - __func__, id, - bulk_packet->endpoint, - bulk_packet->status, - bulk_packet->length, - bulk_packet->stream_id, - bulk_packet->length_high); - dump_data(data, data_len); + read_all(bulk_packet); + read_all(data, data_len); usbredirparser_free_packet_data(parser.get(), data); } @@ -285,12 +224,8 @@ struct usb_redir_iso_packet_header *iso_packet, uint8_t *data, int data_len) { - log("%s: id=%" PRIu64 ", endpoint=%d, status=%d, length=%d\n", - __func__, id, - iso_packet->endpoint, - iso_packet->status, - iso_packet->length); - dump_data(data, data_len); + read_all(iso_packet); + read_all(data, data_len); usbredirparser_free_packet_data(parser.get(), data); } @@ -298,12 +233,8 @@ struct usb_redir_interrupt_packet_header *interrupt_packet, uint8_t *data, int data_len) { - log("%s: id=%" PRIu64 ", endpoint=%d, status=%d, length=%d\n", - __func__, id, - interrupt_packet->endpoint, - interrupt_packet->status, - interrupt_packet->length); - dump_data(data, data_len); + read_all(interrupt_packet); + read_all(data, data_len); usbredirparser_free_packet_data(parser.get(), data); } @@ -311,13 +242,8 @@ struct usb_redir_buffered_bulk_packet_header *buffered_bulk_header, uint8_t *data, int data_len) { - log("%s: stream_id=%d, length=%d, endpoint=%d, status=%d\n", - __func__, id, - buffered_bulk_header->stream_id, - buffered_bulk_header->length, - buffered_bulk_header->endpoint, - buffered_bulk_header->status); - dump_data(data, data_len); + read_all(buffered_bulk_header); + read_all(data, data_len); usbredirparser_free_packet_data(parser.get(), data); } @@ -340,7 +266,6 @@ void parser_hello(void *priv, struct usb_redir_hello_header *h) { - log("%s: %s\n", __func__, h->version); } void parser_device_disconnect_ack(void *priv) @@ -350,16 +275,19 @@ void parser_start_bulk_receiving(void *priv, uint64_t id, struct usb_redir_start_bulk_receiving_header *start_bulk_receiving) { + read_all(start_bulk_receiving); } void parser_stop_bulk_receiving(void *priv, uint64_t id, struct usb_redir_stop_bulk_receiving_header *stop_bulk_receiving) { + read_all(stop_bulk_receiving); } void parser_bulk_receiving_status(void *priv, uint64_t id, struct usb_redir_bulk_receiving_status_header *bulk_receiving_status) { + read_all(bulk_receiving_status); } int try_unserialize(struct usbredirparser *parser, FuzzedDataProvider *fdp) @@ -370,9 +298,11 @@ state.reserve(len); if (len >= 4) { - // Could also move USBREDIRPARSER_SERIALIZE_MAGIC after moving it to - // a shared header. - state.insert(state.end(), {'U', 'R', 'P', '1'}); + const uint32_t magic = USBREDIRPARSER_SERIALIZE_MAGIC; + assert(state.empty()); + state.resize(sizeof(magic)); + memcpy(state.data(), &magic, sizeof(magic)); + len -= 4; } @@ -488,7 +418,6 @@ // Keep reading break; default: - log("usbredirparser_do_read failed: %d\n", ret); goto out; } @@ -500,7 +429,6 @@ while (usbredirparser_has_data_to_write(parser.get())) { ret = usbredirparser_do_write(parser.get()); if (ret < 0) { - log("usbredirparser_do_write failed: %d\n", ret); goto out; } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/meson.build new/usbredir-0.12.0/meson.build --- old/usbredir-0.11.0/meson.build 2021-08-10 08:13:51.747648000 +0200 +++ new/usbredir-0.12.0/meson.build 2021-11-12 11:45:50.003210800 +0100 @@ -1,5 +1,5 @@ -project('usbredir', 'c', 'cpp', - version: '0.11.0', +project('usbredir', 'c', + version: '0.12.0', license: 'LGPLv2.1+', meson_version : '>= 0.53', default_options : [ @@ -15,10 +15,10 @@ '--param=ssp-buffer-size=4', ] if host_machine.system() != 'windows' - cc_flags += [ - '-Wp,-D_FORTIFY_SOURCE=2', - '-fstack-protector', - ] + cc_flags += [ '-Wp,-D_FORTIFY_SOURCE=2' ] + if not get_option('stack_protector').disabled() + cc_flags += [ '-fstack-protector' ] + endif endif # Check if we are building from .git @@ -34,6 +34,10 @@ config = configuration_data() +if get_option('extra-checks') + config.set('ENABLE_EXTRA_CHECKS', '1') +endif + config.set('USBREDIR_VISIBLE', '') foreach visibility : [ '__attribute__((visibility ("default")))', @@ -106,7 +110,9 @@ subdir('fuzzing') endif endif -subdir('tests') +if get_option('tests').enabled() + subdir('tests') +endif subdir('data') summary(summary_info, bool_yn: true) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/meson_options.txt new/usbredir-0.12.0/meson_options.txt --- old/usbredir-0.11.0/meson_options.txt 2021-08-10 08:13:51.747648000 +0200 +++ new/usbredir-0.12.0/meson_options.txt 2021-11-12 11:45:50.003210800 +0100 @@ -17,7 +17,22 @@ type : 'string', description : 'Installation directory for fuzzing binaries') +option('stack_protector', + type : 'feature', + value : 'enabled', + description : 'Build usbredir\'s with stack-protector') + option('tools', type : 'feature', value : 'enabled', description : 'Build usbredir\'s tools such as usbredirect') + +option('tests', + type : 'feature', + value : 'enabled', + description : 'Build usbredir\'s tests such as filter') + +option('extra-checks', + type : 'boolean', + value : false, + description : 'Enable extra checks on code. Do not use for production') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/tools/usbredirect.c new/usbredir-0.12.0/tools/usbredirect.c --- old/usbredir-0.11.0/tools/usbredirect.c 2021-08-10 08:13:51.747648000 +0200 +++ new/usbredir-0.12.0/tools/usbredirect.c 2021-11-12 11:45:50.009210800 +0100 @@ -519,7 +519,7 @@ self, PACKAGE_STRING, self->verbosity, - usbredirhost_fl_write_cb_owns_buffer); + 0); if (!self->usbredirhost) { g_warning("Error starting usbredirhost"); goto err_init; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/usbredirhost/meson.build new/usbredir-0.12.0/usbredirhost/meson.build --- old/usbredir-0.11.0/usbredirhost/meson.build 2021-08-10 08:13:51.747648000 +0200 +++ new/usbredir-0.12.0/usbredirhost/meson.build 2021-11-12 11:45:50.009210800 +0100 @@ -1,7 +1,7 @@ # so verison, see: # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html usbredir_host_current = 1 -usbredir_host_revision = 2 +usbredir_host_revision = 3 usbredir_host_age = 0 usbredir_host_so_version = '@0@.@1@.@2@'.format( usbredir_host_current - usbredir_host_age, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/usbredirhost/usbredirhost.c new/usbredir-0.12.0/usbredirhost/usbredirhost.c --- old/usbredir-0.11.0/usbredirhost/usbredirhost.c 2021-08-10 08:13:51.748648200 +0200 +++ new/usbredir-0.12.0/usbredirhost/usbredirhost.c 2021-11-12 11:45:50.011210700 +0100 @@ -116,6 +116,7 @@ usbredirhost_buffered_output_size buffered_output_size_func; void *func_priv; int verbose; + int flags; libusb_context *ctx; libusb_device *dev; libusb_device_handle *handle; @@ -691,6 +692,7 @@ host->func_priv = func_priv; host->verbose = verbose; host->disconnected = 1; /* No device is connected initially */ + host->flags = flags; host->parser = usbredirparser_create(); if (!host->parser) { log_func(func_priv, usbredirparser_error, @@ -1042,10 +1044,17 @@ { uint64_t size; - if (!host->buffered_output_size_func) - return true; + if (host->flags & usbredirhost_fl_write_cb_owns_buffer) { + if (!host->buffered_output_size_func) { + /* Application is not dropping isoc packages */ + return true; + } + size = host->buffered_output_size_func(host->func_priv); + } else { + /* queue is on usbredirparser */ + size = usbredirparser_get_bufferered_output_size(host->parser); + } - size = host->buffered_output_size_func(host->func_priv); if (size >= host->iso_threshold.higher) { if (!host->iso_threshold.dropping) DEBUG("START dropping isoc packets %" PRIu64 " buffer > %" PRIu64 " hi threshold", @@ -1442,6 +1451,13 @@ return; } + if (!(host->flags & usbredirhost_fl_write_cb_owns_buffer)) { + host->log_func(host->func_priv, usbredirparser_warning, + "can't set callback as usbredirhost owns the output " + "buffer (flag: usbredirhost_fl_write_cb_owns_buffer)"); + return; + } + host->buffered_output_size_func = buffered_output_size_func; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/usbredirhost/usbredirhost.h new/usbredir-0.12.0/usbredirhost/usbredirhost.h --- old/usbredir-0.11.0/usbredirhost/usbredirhost.h 2021-08-10 08:13:51.748648200 +0200 +++ new/usbredir-0.12.0/usbredirhost/usbredirhost.h 2021-11-12 11:45:50.012210800 +0100 @@ -18,8 +18,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef __USBREDIRHOST_H -#define __USBREDIRHOST_H +#pragma once #include <libusb.h> #include "usbredirparser.h" @@ -182,5 +181,3 @@ #ifdef __cplusplus } #endif - -#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/usbredirparser/meson.build new/usbredir-0.12.0/usbredirparser/meson.build --- old/usbredir-0.11.0/usbredirparser/meson.build 2021-08-10 08:13:51.748648200 +0200 +++ new/usbredir-0.12.0/usbredirparser/meson.build 2021-11-12 11:45:50.013210800 +0100 @@ -1,8 +1,8 @@ # so verison, see: # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html -usbredir_parser_current = 2 +usbredir_parser_current = 3 usbredir_parser_revision = 0 -usbredir_parser_age = 1 +usbredir_parser_age = 2 usbredir_parser_so_version = '@0@.@1@.@2@'.format( usbredir_parser_current - usbredir_parser_age, usbredir_parser_age, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/usbredirparser/strtok_r.h new/usbredir-0.12.0/usbredirparser/strtok_r.h --- old/usbredir-0.11.0/usbredirparser/strtok_r.h 2021-08-10 08:13:51.748648200 +0200 +++ new/usbredir-0.12.0/usbredirparser/strtok_r.h 2021-11-12 11:45:50.013210800 +0100 @@ -16,11 +16,7 @@ License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ - -#ifndef _USBREDIRPARSER_STRTOK_H_ -#define _USBREDIRPARSER_STRTOK_H_ +#pragma once char * glibc_strtok_r(char *s, const char *delim, char **save_ptr); - -#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/usbredirparser/usbredirfilter.h new/usbredir-0.12.0/usbredirparser/usbredirfilter.h --- old/usbredir-0.11.0/usbredirparser/usbredirfilter.h 2021-08-10 08:13:51.748648200 +0200 +++ new/usbredir-0.12.0/usbredirparser/usbredirfilter.h 2021-11-12 11:45:50.015210900 +0100 @@ -18,8 +18,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef __USBREDIRFILTER_H -#define __USBREDIRFILTER_H +#pragma once #include <stdio.h> #include <stdint.h> @@ -139,5 +138,3 @@ #ifdef __cplusplus } #endif - -#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/usbredirparser/usbredirparser.c new/usbredir-0.12.0/usbredirparser/usbredirparser.c --- old/usbredir-0.11.0/usbredirparser/usbredirparser.c 2021-08-10 08:13:51.748648200 +0200 +++ new/usbredir-0.12.0/usbredirparser/usbredirparser.c 2021-11-12 11:45:50.017210700 +0100 @@ -20,6 +20,7 @@ */ #include "config.h" +#include <assert.h> #include <stdbool.h> #include <stddef.h> #include <stdio.h> @@ -81,8 +82,9 @@ int data_len; int data_read; int to_skip; - struct usbredirparser_buf *write_buf; int write_buf_count; + struct usbredirparser_buf *write_buf; + uint64_t write_buf_total_size; }; static void @@ -110,6 +112,38 @@ #define INFO(...) va_log(parser, usbredirparser_info, __VA_ARGS__) #define DEBUG(...) va_log(parser, usbredirparser_debug, __VA_ARGS__) +static inline void +usbredirparser_assert_invariants(const struct usbredirparser_priv *parser) +{ +#ifdef ENABLE_EXTRA_CHECKS + assert(parser != NULL); + assert(parser->header_read >= 0); + assert(parser->header_read <= sizeof(parser->header)); + assert(parser->type_header_read >= 0); + assert(parser->type_header_len <= sizeof(parser->type_header)); + assert(parser->type_header_read <= parser->type_header_len); + assert(parser->data_len >= 0); + assert(parser->data_len <= MAX_PACKET_SIZE); + assert(parser->data_read >= 0); + assert(parser->data_read <= parser->data_len); + assert((parser->data_len != 0) ^ (parser->data == NULL)); + + int write_buf_count = 0; + uint64_t total_size = 0; + const struct usbredirparser_buf *write_buf = parser->write_buf; + for (; write_buf != NULL ; write_buf = write_buf->next) { + assert(write_buf->pos >= 0); + assert(write_buf->len >= 0); + assert(write_buf->pos <= write_buf->len); + assert(write_buf->len == 0 || write_buf->buf != NULL); + write_buf_count++; + total_size += write_buf->len; + } + assert(parser->write_buf_count == write_buf_count); + assert(parser->write_buf_total_size == total_size); +#endif +} + #if 0 /* Can be enabled and called from random place to test serialization */ static void serialize_test(struct usbredirparser *parser_pub) { @@ -219,6 +253,19 @@ free(parser); } +USBREDIR_VISIBLE +uint64_t usbredirparser_get_bufferered_output_size(struct usbredirparser *parser_pub) +{ + struct usbredirparser_priv *parser = + (struct usbredirparser_priv *)parser_pub; + uint64_t size; + + LOCK(parser); + size = parser->write_buf_total_size; + UNLOCK(parser); + return size; +} + static int usbredirparser_caps_get_cap(struct usbredirparser_priv *parser, uint32_t *caps, int cap) { @@ -973,13 +1020,16 @@ header_len = usbredirparser_get_header_len(parser_pub); + usbredirparser_assert_invariants(parser); /* Skip forward to next packet (only used in error conditions) */ while (parser->to_skip > 0) { uint8_t buf[65536]; r = (parser->to_skip > sizeof(buf)) ? sizeof(buf) : parser->to_skip; r = parser->callb.read_func(parser->callb.priv, buf, r); - if (r <= 0) + if (r <= 0) { + usbredirparser_assert_invariants(parser); return r; + } parser->to_skip -= r; } @@ -999,6 +1049,7 @@ if (r > 0) { r = parser->callb.read_func(parser->callb.priv, dest, r); if (r <= 0) { + usbredirparser_assert_invariants(parser); return r; } } @@ -1014,6 +1065,7 @@ parser->header.type); parser->to_skip = parser->header.length; parser->header_read = 0; + usbredirparser_assert_invariants(parser); return usbredirparser_read_parse_error; } /* This should never happen */ @@ -1021,6 +1073,7 @@ ERROR("error type specific header buffer too small, please report!!"); parser->to_skip = parser->header.length; parser->header_read = 0; + usbredirparser_assert_invariants(parser); return usbredirparser_read_parse_error; } if (parser->header.length > MAX_PACKET_SIZE) { @@ -1028,6 +1081,7 @@ parser->header.length, MAX_PACKET_SIZE); parser->to_skip = parser->header.length; parser->header_read = 0; + usbredirparser_assert_invariants(parser); return usbredirparser_read_parse_error; } if ((int)parser->header.length < type_header_len || @@ -1037,6 +1091,7 @@ parser->header.type, parser->header.length); parser->to_skip = parser->header.length; parser->header_read = 0; + usbredirparser_assert_invariants(parser); return usbredirparser_read_parse_error; } data_len = parser->header.length - type_header_len; @@ -1046,6 +1101,7 @@ ERROR("Out of memory allocating data buffer"); parser->to_skip = parser->header.length; parser->header_read = 0; + usbredirparser_assert_invariants(parser); return usbredirparser_read_parse_error; } } @@ -1074,8 +1130,10 @@ parser->data_len = 0; parser->data_read = 0; parser->data = NULL; - if (!r) + if (!r) { + usbredirparser_assert_invariants(parser); return usbredirparser_read_parse_error; + } /* header len may change if this was an hello packet */ header_len = usbredirparser_get_header_len(parser_pub); } @@ -1100,6 +1158,8 @@ int w, ret = 0; LOCK(parser); + assert((parser->write_buf_count != 0) ^ (parser->write_buf == NULL)); + for (;;) { wbuf = parser->write_buf; if (!wbuf) @@ -1123,8 +1183,10 @@ parser->write_buf = wbuf->next; if (!(parser->flags & usbredirparser_fl_write_cb_owns_buffer)) free(wbuf->buf); - free(wbuf); + + parser->write_buf_total_size -= wbuf->len; parser->write_buf_count--; + free(wbuf); } } UNLOCK(parser); @@ -1154,7 +1216,7 @@ uint8_t *buf, *type_header_out, *data_out; struct usb_redir_header *header; struct usbredirparser_buf *wbuf, *new_wbuf; - int header_len, type_header_len; + int header_len, type_header_len, total_size; header_len = usbredirparser_get_header_len(parser_pub); type_header_len = usbredirparser_get_type_header_len(parser_pub, type, 1); @@ -1169,8 +1231,9 @@ return; } + total_size = header_len + type_header_len + data_len; new_wbuf = calloc(1, sizeof(*new_wbuf)); - buf = malloc(header_len + type_header_len + data_len); + buf = malloc(total_size); if (!new_wbuf || !buf) { ERROR("Out of memory allocating buffer to send packet, dropping!"); free(new_wbuf); free(buf); @@ -1178,7 +1241,7 @@ } new_wbuf->buf = buf; - new_wbuf->len = header_len + type_header_len + data_len; + new_wbuf->len = total_size; header = (struct usb_redir_header *)buf; type_header_out = buf + header_len; @@ -1204,6 +1267,7 @@ wbuf->next = new_wbuf; } + parser->write_buf_total_size += total_size; parser->write_buf_count++; UNLOCK(parser); } @@ -1496,7 +1560,6 @@ /****** Serialization support ******/ -#define USBREDIRPARSER_SERIALIZE_MAGIC 0x55525031 #define USBREDIRPARSER_SERIALIZE_BUF_SIZE 65536 /* Serialization format, send and receiving endian are expected to be the same! @@ -1742,25 +1805,43 @@ uint8_t *data; uint32_t i, l, header_len, remain = len; - if (unserialize_int(parser, &state, &remain, &i, "magic")) + usbredirparser_assert_invariants(parser); + if (unserialize_int(parser, &state, &remain, &i, "magic")) { + usbredirparser_assert_invariants(parser); return -1; + } if (i != USBREDIRPARSER_SERIALIZE_MAGIC) { ERROR("error unserialize magic mismatch"); + usbredirparser_assert_invariants(parser); return -1; } - if (unserialize_int(parser, &state, &remain, &i, "length")) + if (!(parser->write_buf_count == 0 && parser->write_buf == NULL && + parser->write_buf_total_size == 0 && + parser->data == NULL && parser->header_read == 0 && + parser->type_header_read == 0 && parser->data_read == 0)) { + ERROR("unserialization must use a pristine parser"); + usbredirparser_assert_invariants(parser); return -1; + } + + if (unserialize_int(parser, &state, &remain, &i, "length")) { + usbredirparser_assert_invariants(parser); + return -1; + } if (i != len) { ERROR("error unserialize length mismatch"); + usbredirparser_assert_invariants(parser); return -1; } data = (uint8_t *)parser->our_caps; i = USB_REDIR_CAPS_SIZE * sizeof(int32_t); memcpy(orig_caps, parser->our_caps, i); - if (unserialize_data(parser, &state, &remain, &data, &i, "our_caps")) + if (unserialize_data(parser, &state, &remain, &data, &i, "our_caps")) { + usbredirparser_assert_invariants(parser); return -1; + } for (i =0; i < USB_REDIR_CAPS_SIZE; i++) { if (parser->our_caps[i] != orig_caps[i]) { /* orig_caps is our original settings @@ -1772,6 +1853,7 @@ /* Source has a cap we don't */ ERROR("error unserialize caps mismatch ours: %x recv: %x", orig_caps[i], parser->our_caps[i]); + usbredirparser_assert_invariants(parser); return -1; } else { /* We've got a cap the source doesn't - that's OK */ @@ -1783,80 +1865,136 @@ data = (uint8_t *)parser->peer_caps; i = USB_REDIR_CAPS_SIZE * sizeof(int32_t); - if (unserialize_data(parser, &state, &remain, &data, &i, "peer_caps")) + if (unserialize_data(parser, &state, &remain, &data, &i, "peer_caps")) { + usbredirparser_assert_invariants(parser); return -1; + } if (i) parser->have_peer_caps = 1; - if (unserialize_int(parser, &state, &remain, &i, "skip")) + if (unserialize_int(parser, &state, &remain, &i, "skip")) { + usbredirparser_assert_invariants(parser); return -1; + } parser->to_skip = i; header_len = usbredirparser_get_header_len(parser_pub); data = (uint8_t *)&parser->header; i = header_len; - if (unserialize_data(parser, &state, &remain, &data, &i, "header")) + if (unserialize_data(parser, &state, &remain, &data, &i, "header")) { + usbredirparser_assert_invariants(parser); return -1; + } parser->header_read = i; + parser->type_header_len = 0; - /* Set various length field froms the header (if we've a header) */ + /* Set various length field from the header (if any) */ if (parser->header_read == header_len) { - int type_header_len = - usbredirparser_get_type_header_len(parser_pub, - parser->header.type, 0); - if (type_header_len < 0 || - type_header_len > sizeof(parser->type_header) || - parser->header.length < type_header_len || - (parser->header.length > type_header_len && - !usbredirparser_expect_extra_data(parser))) { - ERROR("error unserialize packet header invalid"); - return -1; - } - parser->type_header_len = type_header_len; - parser->data_len = parser->header.length - type_header_len; + if (parser->header.length > MAX_PACKET_SIZE) { + ERROR("packet length of %d larger than permitted %d bytes", + parser->header.length, MAX_PACKET_SIZE); + return -1; + } + + int type_header_len = + usbredirparser_get_type_header_len(parser_pub, + parser->header.type, 0); + if (type_header_len < 0 || + type_header_len > sizeof(parser->type_header) || + parser->header.length < type_header_len || + (parser->header.length > type_header_len && + !usbredirparser_expect_extra_data(parser))) { + ERROR("error unserialize packet header invalid"); + usbredirparser_assert_invariants(parser); + return -1; + } + parser->type_header_len = type_header_len; } data = parser->type_header; i = parser->type_header_len; - if (unserialize_data(parser, &state, &remain, &data, &i, "type_header")) + if (unserialize_data(parser, &state, &remain, &data, &i, "type_header")) { + usbredirparser_assert_invariants(parser); return -1; - parser->type_header_read = i; + } + if (parser->header_read == header_len) { + parser->type_header_read = i; + } - if (parser->data_len) { - parser->data = malloc(parser->data_len); - if (!parser->data) { - ERROR("Out of memory allocating unserialize buffer"); - return -1; + if (parser->type_header_read == parser->type_header_len) { + parser->data_len = parser->header.length - parser->type_header_len; + if (parser->data_len) { + parser->data = malloc(parser->data_len); + if (!parser->data) { + ERROR("Out of memory allocating unserialize buffer"); + usbredirparser_assert_invariants(parser); + return -1; + } } } i = parser->data_len; - if (unserialize_data(parser, &state, &remain, &parser->data, &i, "data")) + if (unserialize_data(parser, &state, &remain, &parser->data, &i, "data")) { + free(parser->data); + parser->data = NULL; + parser->data_len = 0; + usbredirparser_assert_invariants(parser); return -1; - parser->data_read = i; + } + if (parser->header_read == header_len && + parser->type_header_read == parser->type_header_len && + parser->data_len > 0) { + parser->data_read = i; + } else if (parser->data != NULL) { + free(parser->data); + parser->data = NULL; + parser->data_len = 0; + } /* Get the write buffer count and the write buffers */ - if (unserialize_int(parser, &state, &remain, &i, "write_buf_count")) + if (unserialize_int(parser, &state, &remain, &i, "write_buf_count")) { + usbredirparser_assert_invariants(parser); return -1; + } next = &parser->write_buf; + usbredirparser_assert_invariants(parser); while (i) { + uint8_t *buf = NULL; + + l = 0; + if (unserialize_data(parser, &state, &remain, &buf, &l, "wbuf")) { + usbredirparser_assert_invariants(parser); + return -1; + } + + if (l == 0) { + free(buf); + ERROR("write buffer %d is empty", i); + usbredirparser_assert_invariants(parser); + return -1; + } + wbuf = calloc(1, sizeof(*wbuf)); if (!wbuf) { + free(buf); ERROR("Out of memory allocating unserialize buffer"); + usbredirparser_assert_invariants(parser); return -1; } - *next = wbuf; - l = 0; - if (unserialize_data(parser, &state, &remain, &wbuf->buf, &l, "wbuf")) - return -1; + wbuf->buf = buf; wbuf->len = l; + *next = wbuf; next = &wbuf->next; + parser->write_buf_total_size += wbuf->len; + parser->write_buf_count++; i--; } if (remain) { ERROR("error unserialize %d bytes of extraneous state data", remain); + usbredirparser_assert_invariants(parser); return -1; } + usbredirparser_assert_invariants(parser); return 0; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/usbredirparser/usbredirparser.h new/usbredir-0.12.0/usbredirparser/usbredirparser.h --- old/usbredir-0.11.0/usbredirparser/usbredirparser.h 2021-08-10 08:13:51.749648000 +0200 +++ new/usbredir-0.12.0/usbredirparser/usbredirparser.h 2021-11-12 11:45:50.018210600 +0100 @@ -18,11 +18,12 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef __USBREDIRPARSER_H -#define __USBREDIRPARSER_H +#pragma once #include "usbredirproto.h" +#define USBREDIRPARSER_SERIALIZE_MAGIC 0x55525031 + #ifdef __cplusplus extern "C" { #endif @@ -244,6 +245,11 @@ /* This returns the number of usbredir packets queued up for writing */ int usbredirparser_has_data_to_write(struct usbredirparser *parser); +/* This returns the number of bytes queued to be written out. Can be used by control plane + * to drop data from being queued, see issue: + * https://gitlab.freedesktop.org/spice/usbredir/-/issues/19 */ +uint64_t usbredirparser_get_bufferered_output_size(struct usbredirparser *parser_pub); + /* Call this when usbredirparser_has_data_to_write returns > 0 returns 0 on success, -1 if a write error happened. If a write error happened, this function will retry writing any queued data @@ -382,5 +388,3 @@ #ifdef __cplusplus } #endif - -#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/usbredirparser/usbredirparser.map new/usbredir-0.12.0/usbredirparser/usbredirparser.map --- old/usbredir-0.11.0/usbredirparser/usbredirparser.map 2021-08-10 08:13:51.749648000 +0200 +++ new/usbredir-0.12.0/usbredirparser/usbredirparser.map 2021-11-12 11:45:50.019210800 +0100 @@ -59,4 +59,10 @@ usbredirfilter_free; } USBREDIRPARSER_0.8.0; +USBREDIRPARSER_0.11.0 { +global: + usbredirparser_get_bufferered_output_size; +} USBREDIRPARSER_0.10.0; + + # .... define new API here using predicted next version number .... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/usbredirparser/usbredirproto-compat.h new/usbredir-0.12.0/usbredirparser/usbredirproto-compat.h --- old/usbredir-0.11.0/usbredirparser/usbredirproto-compat.h 2021-08-10 08:13:51.749648000 +0200 +++ new/usbredir-0.12.0/usbredirparser/usbredirproto-compat.h 2021-11-12 11:45:50.019210800 +0100 @@ -18,8 +18,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef __USBREDIRPROTO_COMPAT_H -#define __USBREDIRPROTO_COMPAT_H +#pragma once /* PACK macros borrowed from spice-protocol */ #ifdef __GNUC__ @@ -82,5 +81,3 @@ #if defined(__MINGW32__) || !defined(__GNUC__) #pragma pack(pop) #endif - -#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/usbredir-0.11.0/usbredirparser/usbredirproto.h new/usbredir-0.12.0/usbredirparser/usbredirproto.h --- old/usbredir-0.11.0/usbredirparser/usbredirproto.h 2021-08-10 08:13:51.749648000 +0200 +++ new/usbredir-0.12.0/usbredirparser/usbredirproto.h 2021-11-12 11:45:50.020210700 +0100 @@ -18,8 +18,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef __USBREDIRPROTO_H -#define __USBREDIRPROTO_H +#pragma once /* PACK macros borrowed from spice-protocol */ #ifdef __GNUC__ @@ -303,5 +302,3 @@ #ifdef __cplusplus } #endif - -#endif