Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libupnp for openSUSE:Factory checked 
in at 2026-09-30 16:22:44
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libupnp (Old)
 and      /work/SRC/openSUSE:Factory/.libupnp.new.1465845 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libupnp"

Wed Sep 30 16:22:44 2026 rev:53 rq:1381625 version:22.1.7

Changes:
--------
--- /work/SRC/openSUSE:Factory/libupnp/libupnp.changes  2026-09-28 
10:38:44.994591088 +0200
+++ /work/SRC/openSUSE:Factory/.libupnp.new.1465845/libupnp.changes     
2026-09-30 16:23:54.800507146 +0200
@@ -1,0 +2,15 @@
+Tue Sep 29 16:53:50 UTC 2026 - Jan Engelhardt <[email protected]>
+
+- Update to release 22.1.7
+  * Fix a memory leak when a GENA subscription is freed.
+    [GHSA-h9f5-9vwp-h89q]
+
+-------------------------------------------------------------------
+Mon Sep 28 17:55:52 UTC 2026 - Jan Engelhardt <[email protected]>
+
+- Update to release 22.1.6
+  * GHSA-mhhw-gm73-c57g: Fix a heap over-read when parsing the
+    Callback header of an incoming GENA SUBSCRIBE request.
+    [GHSA-mhhw-gm73-c57g]
+
+-------------------------------------------------------------------

Old:
----
  libupnp-22.1.5.tar.bz2

New:
----
  libupnp-22.1.7.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libupnp.spec ++++++
--- /var/tmp/diff_new_pack.CFgCkX/_old  2026-09-30 16:23:55.473535171 +0200
+++ /var/tmp/diff_new_pack.CFgCkX/_new  2026-09-30 16:23:55.475535254 +0200
@@ -19,7 +19,7 @@
 
 %define pnpver 22
 Name:           libupnp
-Version:        22.1.5
+Version:        22.1.7
 Release:        0
 Summary:        An implementation of Universal Plug and Play (UPnP)
 License:        BSD-3-Clause

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.CFgCkX/_old  2026-09-30 16:23:55.516536961 +0200
+++ /var/tmp/diff_new_pack.CFgCkX/_new  2026-09-30 16:23:55.520537128 +0200
@@ -1,5 +1,5 @@
-mtime: 1790373040
-commit: 0884d08aaa81307576fcb1ac2a591954268214ee78fb89b8ae4a34c7eedfdd94
+mtime: 1790700851
+commit: 21ad2a57bf59f5cb03c24b14dbd868634cafc7b55f6806abe0e73b4a3d8b4e64
 url: https://src.opensuse.org/jengelh/libupnp
 revision: master
 

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-09-29 18:54:11.000000000 +0200
@@ -0,0 +1 @@
+.osc

++++++ libupnp-22.1.5.tar.bz2 -> libupnp-22.1.7.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libupnp-22.1.5/CMakeLists.txt 
new/libupnp-22.1.7/CMakeLists.txt
--- old/libupnp-22.1.5/CMakeLists.txt   2026-09-25 21:45:40.000000000 +0200
+++ new/libupnp-22.1.7/CMakeLists.txt   2026-09-29 17:27:28.000000000 +0200
@@ -7,7 +7,7 @@
 set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 
 project(PUPNP
-       VERSION 22.1.5
+       VERSION 22.1.7
        LANGUAGES C)
 
 include(GNUInstallDirs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libupnp-22.1.5/ChangeLog new/libupnp-22.1.7/ChangeLog
--- old/libupnp-22.1.5/ChangeLog        2026-09-25 21:45:40.000000000 +0200
+++ new/libupnp-22.1.7/ChangeLog        2026-09-29 17:27:28.000000000 +0200
@@ -1,4 +1,31 @@
 *******************************************************************************
+Version 22.1.7
+*******************************************************************************
+
+- GHSA-h9f5-9vwp-h89q: Fix a memory leak when a GENA subscription is freed.
+  The subscription's event queue recycles its list nodes: ListDelNode()
+  keeps them on the list's own free list, and only ListDestroy() releases
+  them. freeSubscription() drained the queue but never called
+  ListDestroy(), so every subscription a device accepted leaked at least
+  one list node when it was torn down, on UNSUBSCRIBE, on expiry or when
+  the device unregistered (CWE-401). A remote peer could repeat
+  SUBSCRIBE/UNSUBSCRIBE without credentials and grow the device's memory
+  without bound. Found and fixed by Damien Plisson (@damien78).
+
+
+*******************************************************************************
+Version 22.1.6
+*******************************************************************************
+
+- GHSA-mhhw-gm73-c57g: Fix a heap over-read when parsing the Callback header
+  of an incoming GENA SUBSCRIBE request. is_mark() and is_reserved() used
+  strchr(), which also matches the terminating NUL, so URI parsing did not
+  stop at the end of the string, and create_url_list() passed parse_uri() a
+  length two bytes too long. A Callback URL without the closing '>' read one
+  byte past its buffer (CWE-125). Found and fixed by @las7 (#641).
+
+
+*******************************************************************************
 Version 22.1.5
 *******************************************************************************
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libupnp-22.1.5/README.md new/libupnp-22.1.7/README.md
--- old/libupnp-22.1.5/README.md        2026-09-25 21:45:40.000000000 +0200
+++ new/libupnp-22.1.7/README.md        2026-09-29 17:27:28.000000000 +0200
@@ -108,6 +108,8 @@
 
 | Release Number | Date       | History                                  |
 | -------------- | ---------- | ---------------------------------------- |
+| 22.1.6         | 2026-09-27 | [Portable UPnP SDK][Portable UPnP SDK]   |
+| 22.1.5         | 2026-09-25 | [Portable UPnP SDK][Portable UPnP SDK]   |
 | 22.1.4         | 2026-09-24 | [Portable UPnP SDK][Portable UPnP SDK]   |
 | 22.1.3         | 2026-09-24 | [Portable UPnP SDK][Portable UPnP SDK]   |
 | 22.1.2         | 2026-09-23 | [Portable UPnP SDK][Portable UPnP SDK]   |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libupnp-22.1.5/docs/Doxyfile 
new/libupnp-22.1.7/docs/Doxyfile
--- old/libupnp-22.1.5/docs/Doxyfile    2026-09-25 21:45:40.000000000 +0200
+++ new/libupnp-22.1.7/docs/Doxyfile    2026-09-29 17:27:28.000000000 +0200
@@ -38,7 +38,7 @@
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 22.1.5
+PROJECT_NUMBER         = 22.1.7
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libupnp-22.1.5/gtest/CMakeLists.txt 
new/libupnp-22.1.7/gtest/CMakeLists.txt
--- old/libupnp-22.1.5/gtest/CMakeLists.txt     2026-09-25 21:45:40.000000000 
+0200
+++ new/libupnp-22.1.7/gtest/CMakeLists.txt     2026-09-29 17:27:28.000000000 
+0200
@@ -62,6 +62,29 @@
        )
 endif()
 
+# regression test — freeSubscription() must free the list nodes its event
+# queue parked for reuse; freeSubscription() and copy_subscription() are
+# internal symbols so only the static variant is built.
+if (NOT WIN32 AND UPNP_BUILD_STATIC)
+       add_executable(test_service_table-static test_service_table.cpp)
+       target_link_libraries(test_service_table-static
+               PRIVATE upnp_static GTest::gtest)
+       target_include_directories(test_service_table-static PRIVATE
+               ${CMAKE_CURRENT_SOURCE_DIR}/../upnp/src/inc/
+               ${CMAKE_CURRENT_SOURCE_DIR}/../upnp/src/threadutil/
+       )
+       if(HAVE_MACRO_PREFIX_MAP)
+               target_compile_options(test_service_table-static
+                       PRIVATE -fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=
+               )
+       endif()
+       gtest_add_tests(
+               TARGET test_service_table-static
+               TEST_PREFIX test-upnp-
+               TEST_SUFFIX -static
+       )
+endif()
+
 # regression test — Windows UpnpGetIfInfo() must not format an address
 # family that was not found on the interface (amule-org/amule#242, #301);
 # UpnpSetIfAddrStrings() is an internal symbol so only the static variant
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libupnp-22.1.5/gtest/test_service_table.cpp 
new/libupnp-22.1.7/gtest/test_service_table.cpp
--- old/libupnp-22.1.5/gtest/test_service_table.cpp     1970-01-01 
01:00:00.000000000 +0100
+++ new/libupnp-22.1.7/gtest/test_service_table.cpp     2026-09-29 
17:27:28.000000000 +0200
@@ -0,0 +1,98 @@
+// regression: freeSubscription() must release the list nodes its event
+// queue parked for reuse.
+//
+// The LinkedList recycles its nodes: ListDelNode() parks a node on the
+// list's own free list, and only ListDestroy() frees the parked ones.
+// freeSubscription() drained sub->outgoing with ListDelNode(), through
+// freeSubscriptionQueuedEvents(), and never called ListDestroy(), so every
+// subscription that had queued an event leaked at least one ListNode when
+// it was torn down.
+//
+// These tests check the free list directly, so they fail without the fix
+// on any build; under LeakSanitizer the lost nodes are also reported.
+
+#include "gtest/gtest.h"
+
+extern "C" {
+#include "ThreadPool.h"
+#include "service_table.h"
+}
+
+#include <cstdlib>
+#include <cstring>
+
+#if defined(INCLUDE_DEVICE_APIS) && EXCLUDE_GENA == 0
+
+// A queued event as genaInitNotifyCommon() stores it: a heap-allocated
+// ThreadPoolJob. freeSubscriptionQueuedEvents() frees the job itself, and
+// looks at job->arg only for the entries after the first.
+static ThreadPoolJob *new_queued_job()
+{
+       return static_cast<ThreadPoolJob *>(calloc(1, sizeof(ThreadPoolJob)));
+}
+
+TEST(FreeSubscription, releases_the_nodes_its_event_queue_recycled)
+{
+       subscription sub{};
+       ASSERT_EQ(ListInit(&sub.outgoing, nullptr, free), 0);
+
+       // Two queued events, then the head sent and removed the way
+       // genaNotifyThread() does it: its node is parked, not freed.
+       ListNode *head = ListAddTail(&sub.outgoing, new_queued_job());
+       ASSERT_NE(head, nullptr);
+       ASSERT_NE(ListAddTail(&sub.outgoing, new_queued_job()), nullptr);
+       ListDelNode(&sub.outgoing, head, 1);
+       ASSERT_EQ(sub.outgoing.freeNodeList.freeListLength, 1);
+
+       freeSubscription(&sub);
+
+       // Without the fix, both nodes are still parked here -- the sent one
+       // and the one freeSubscriptionQueuedEvents() removed -- and they are
+       // lost once the subscription itself is freed.
+       EXPECT_EQ(sub.outgoing.freeNodeList.freeListLength, 0);
+       EXPECT_EQ(sub.outgoing.freeNodeList.head, nullptr);
+}
+
+TEST(FreeSubscription, is_safe_on_a_copy_made_by_copy_subscription)
+{
+       // genaNotifyThread() sends each event from a stack copy of the
+       // subscription, frees it with freeSubscription(), and
+       // copy_subscription() gives that copy an empty event queue of its own.
+       static const char callback[] = "<http://192.168.0.2:49152/cb>";
+       subscription sub{};
+       subscription copy{};
+
+       ASSERT_EQ(ListInit(&sub.outgoing, nullptr, free), 0);
+       sub.DeliveryURLs.URLs = strdup(callback);
+       sub.DeliveryURLs.parsedURLs =
+               static_cast<uri_type *>(calloc(1, sizeof(uri_type)));
+       ASSERT_NE(sub.DeliveryURLs.URLs, nullptr);
+       ASSERT_NE(sub.DeliveryURLs.parsedURLs, nullptr);
+       ASSERT_EQ(parse_uri(sub.DeliveryURLs.URLs + 1,
+                         strlen(callback) - 2,
+                         &sub.DeliveryURLs.parsedURLs[0]),
+               HTTP_SUCCESS);
+       sub.DeliveryURLs.size = 1;
+       ASSERT_NE(ListAddTail(&sub.outgoing, new_queued_job()), nullptr);
+
+       ASSERT_EQ(copy_subscription(&sub, &copy), HTTP_SUCCESS);
+       EXPECT_EQ(ListSize(&copy.outgoing), 0);
+
+       freeSubscription(&copy);
+       EXPECT_EQ(copy.outgoing.freeNodeList.freeListLength, 0);
+       EXPECT_EQ(copy.outgoing.freeNodeList.head, nullptr);
+
+       // The original is untouched by freeing the copy.
+       EXPECT_EQ(ListSize(&sub.outgoing), 1);
+       freeSubscription(&sub);
+       EXPECT_EQ(sub.outgoing.freeNodeList.freeListLength, 0);
+       EXPECT_EQ(sub.outgoing.freeNodeList.head, nullptr);
+}
+
+#endif /* INCLUDE_DEVICE_APIS && EXCLUDE_GENA == 0 */
+
+int main(int argc, char **argv)
+{
+       ::testing::InitGoogleTest(&argc, argv);
+       return RUN_ALL_TESTS();
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libupnp-22.1.5/upnp/src/gena/gena_device.c 
new/libupnp-22.1.7/upnp/src/gena/gena_device.c
--- old/libupnp-22.1.5/upnp/src/gena/gena_device.c      2026-09-25 
21:45:40.000000000 +0200
+++ new/libupnp-22.1.7/upnp/src/gena/gena_device.c      2026-09-29 
17:27:28.000000000 +0200
@@ -1175,7 +1175,7 @@
        for (i = 0; i < URLS->size; i++) {
                if ((URLS->buff[i] == '<') && (i + 1 < URLS->size)) {
                        if (((return_code = parse_uri(&URLS->buff[i + 1],
-                                     URLS->size - i + 1,
+                                     URLS->size - i - 1,
                                      &temp)) == HTTP_SUCCESS) &&
                                (temp.hostport.text.size != 0) &&
                                (temp.hostport.IPaddress.ss_family !=
@@ -1204,7 +1204,7 @@
                for (i = 0; i < URLS->size; i++) {
                        if ((URLS->buff[i] == '<') && (i + 1 < URLS->size)) {
                                if (((return_code = parse_uri(&out->URLs[i + 1],
-                                             URLS->size - i + 1,
+                                             URLS->size - i - 1,
                                              &out->parsedURLs[URLcount2])) ==
                                            HTTP_SUCCESS) &&
                                        (out->parsedURLs[URLcount2]
@@ -1239,9 +1239,9 @@
                        }
                }
        }
-       out->size = URLcount;
+       out->size = URLcount2;
 
-       return (int)URLcount;
+       return (int)URLcount2;
 }
 
 /*!
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libupnp-22.1.5/upnp/src/genlib/net/uri/uri.c 
new/libupnp-22.1.7/upnp/src/genlib/net/uri/uri.c
--- old/libupnp-22.1.5/upnp/src/genlib/net/uri/uri.c    2026-09-25 
21:45:40.000000000 +0200
+++ new/libupnp-22.1.7/upnp/src/genlib/net/uri/uri.c    2026-09-29 
17:27:28.000000000 +0200
@@ -76,7 +76,7 @@
        /*! [in] Char to be matched for RESERVED characters. */
        char in)
 {
-       if (strchr(RESERVED, (int)in)) {
+       if (in != '\0' && strchr(RESERVED, (int)in)) {
                return 1;
        } else {
                return 0;
@@ -93,7 +93,7 @@
        /*! [in] Char to be matched for MARKED characters. */
        char in)
 {
-       if (strchr(MARK, (int)in)) {
+       if (in != '\0' && strchr(MARK, (int)in)) {
                return 1;
        } else {
                return 0;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/libupnp-22.1.5/upnp/src/genlib/service_table/service_table.c 
new/libupnp-22.1.7/upnp/src/genlib/service_table/service_table.c
--- old/libupnp-22.1.5/upnp/src/genlib/service_table/service_table.c    
2026-09-25 21:45:40.000000000 +0200
+++ new/libupnp-22.1.7/upnp/src/genlib/service_table/service_table.c    
2026-09-29 17:27:28.000000000 +0200
@@ -205,6 +205,10 @@
        if (sub) {
                free_URL_list(&sub->DeliveryURLs);
                freeSubscriptionQueuedEvents(sub);
+               /* ListDelNode() only parks the list nodes on the list's own
+                * free list for reuse; ListDestroy() is what releases them.
+                * The items are already freed, so freeItem is 0. */
+               ListDestroy(&sub->outgoing, 0);
        }
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libupnp-22.1.5/upnp/test/poc_gh_325.c 
new/libupnp-22.1.7/upnp/test/poc_gh_325.c
--- old/libupnp-22.1.5/upnp/test/poc_gh_325.c   2026-09-25 21:45:40.000000000 
+0200
+++ new/libupnp-22.1.7/upnp/test/poc_gh_325.c   2026-09-29 17:27:28.000000000 
+0200
@@ -29,6 +29,7 @@
        #include <stddef.h>
        #include <string.h>
        #include <sys/socket.h>
+       #include <sys/time.h>
        #include <unistd.h>
 
 /* regression: issue #325 -- test hook exported from libupnp */
@@ -48,6 +49,12 @@
        if (sock < 0)
                return;
 
+       /* Once UpnpFinish() has released the port, connecting to it can end
+        * in a TCP self-connect (the port is in the ephemeral range), and
+        * recv() would then wait forever for a peer that is this socket. */
+       struct timeval tv = {1, 0};
+       setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
+
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof addr);
        addr.sin_family = AF_INET;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libupnp-22.1.5/upnp/test/test_parse_uri.c 
new/libupnp-22.1.7/upnp/test/test_parse_uri.c
--- old/libupnp-22.1.5/upnp/test/test_parse_uri.c       2026-09-25 
21:45:40.000000000 +0200
+++ new/libupnp-22.1.7/upnp/test/test_parse_uri.c       2026-09-29 
17:27:28.000000000 +0200
@@ -198,6 +198,42 @@
        return 0;
 }
 
+/*
+ * Regression test for PR #641: is_mark() and is_reserved() used
+ * strchr(SET, in), which matches the terminating NUL, so parse_uric() treated
+ * '\0' as a URI character and scanned past the end of the string whenever
+ * the caller's length overshot it. create_url_list() did exactly that with
+ * an unterminated GENA Callback header ("<http://host/path"; with no '>'),
+ * causing a heap over-read (CWE-125).
+ *
+ * The length passed here covers the NUL plus one non-uric byte, so the
+ * buffer is never overrun; the path must stop at the NUL ("/a", size 2).
+ */
+static int check_nul_terminates_uri(void)
+{
+       uri_type url;
+       static const char s[] = "http://192.0.2.1/a\0 ";
+
+       if (parse_uri(s, sizeof(s) - 1, &url) != HTTP_SUCCESS) {
+               printf("%s:%d parse_uri('%s') failed to parse a valid URL\n",
+                       __FILE__,
+                       __LINE__,
+                       s);
+               return 1;
+       }
+       if (url.pathquery.size != 2) {
+               printf("%s:%d parse_uri('%s') pathquery.size = %d, expected 2 "
+                      "-- the NUL terminator must not be a URI character\n",
+                       __FILE__,
+                       __LINE__,
+                       s,
+                       (int)url.pathquery.size);
+               return 1;
+       }
+
+       return 0;
+}
+
 int main(void)
 {
        int i;
@@ -230,6 +266,7 @@
        failures += check_no_eager_resolution();
        failures += check_literal_ip_is_noop();
        failures += check_resolve_hostport_localhost();
+       failures += check_nul_terminates_uri();
 
 #ifdef _WIN32
        WSACleanup();

Reply via email to