first commit

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-28 09:16:54 +01:00
commit 32a7ba8e8f
168 files changed
+123856

No files matched your search

+293
View File
@@ -0,0 +1,293 @@
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,17 +1,8 @@
-# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
-#
-# When uploading crates to the registry Cargo will automatically
-# "normalize" Cargo.toml files for maximal compatibility
-# with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies.
-#
-# If you are reading this file be aware that the original Cargo.toml
-# will likely look very different (and much more reasonable).
-# See Cargo.toml.orig for the original contents.
-
+# Vendored from crates.io libssh2-sys 0.3.3; see PROVENANCE.md.
[package]
name = "libssh2-sys"
version = "0.3.3"
+publish = false
authors = [
"Alex Crichton <alex@alexcrichton.com>",
"Wez Furlong <wez@wezfurlong.org>",
@@ -50,12 +41,6 @@
[build-dependencies.cc]
version = "1.0.25"
-[build-dependencies.pkg-config]
-version = "0.3.11"
-
-[target.'cfg(target_env = "msvc")'.build-dependencies.vcpkg]
-version = "0.2"
-
[target."cfg(unix)".dependencies.openssl-sys]
version = "0.9.35"
--- a/build.rs
+++ b/build.rs
@@ -1,42 +1,19 @@
extern crate cc;
-extern crate pkg_config;
-
-#[cfg(target_env = "msvc")]
-extern crate vcpkg;
use std::env;
use std::fs;
-use std::path::{Path, PathBuf};
-use std::process::Command;
+use std::path::PathBuf;
fn main() {
- let zlib_ng_compat = env::var("CARGO_FEATURE_ZLIB_NG_COMPAT").is_ok();
-
- if !zlib_ng_compat && try_vcpkg() {
- return;
+ // This distribution contains reviewed security backports that an
+ // arbitrary system libssh2 may lack. Always compile this source tree.
+ println!("cargo:rerun-if-env-changed=LIBSSH2_SYS_USE_PKG_CONFIG");
+ if env::var_os("LIBSSH2_SYS_USE_PKG_CONFIG").is_some() {
+ panic!("This distribution requires its patched bundled libssh2; unset LIBSSH2_SYS_USE_PKG_CONFIG");
}
-
- // The system copy of libssh2 is not used by default because it
- // can lead to having two copies of libssl loaded at once.
- // See https://github.com/alexcrichton/ssh2-rs/pull/88
- println!("cargo:rerun-if-env-changed=LIBSSH2_SYS_USE_PKG_CONFIG");
- if env::var("LIBSSH2_SYS_USE_PKG_CONFIG").is_ok() {
- if zlib_ng_compat {
- panic!("LIBSSH2_SYS_USE_PKG_CONFIG set, but cannot use zlib-ng-compat with system libssh2");
- }
- if let Ok(lib) = pkg_config::find_library("libssh2") {
- for path in &lib.include_paths {
- println!("cargo:include={}", path.display());
- }
- return;
- }
- }
-
- if !Path::new("libssh2/.git").exists() {
- let _ = Command::new("git")
- .args(&["submodule", "update", "--init"])
- .status();
- }
+ println!("cargo:rerun-if-changed=libssh2/src");
+ println!("cargo:rerun-if-changed=libssh2/include");
+ println!("cargo:rerun-if-changed=libssh2/libssh2.pc.in");
let target = env::var("TARGET").unwrap();
let profile = env::var("PROFILE").unwrap();
@@ -201,45 +178,3 @@
}
}
-#[cfg(not(target_env = "msvc"))]
-fn try_vcpkg() -> bool {
- false
-}
-
-#[cfg(target_env = "msvc")]
-fn try_vcpkg() -> bool {
- vcpkg::Config::new()
- .emit_includes(true)
- .probe("libssh2")
- .map(|_| {
- // found libssh2 which depends on openssl and zlib
- vcpkg::Config::new()
- .lib_name("libssl")
- .lib_name("libcrypto")
- .probe("openssl")
- .or_else(|_| {
- // openssl 1.1 was not found, try openssl 1.0
- vcpkg::Config::new()
- .lib_name("libeay32")
- .lib_name("ssleay32")
- .probe("openssl")
- })
- .expect(
- "configured libssh2 from vcpkg but could not \
- find openssl libraries that it depends on",
- );
-
- vcpkg::Config::new()
- .lib_names("zlib", "zlib1")
- .probe("zlib")
- .expect(
- "configured libssh2 from vcpkg but could not \
- find the zlib library that it depends on",
- );
-
- println!("cargo:rustc-link-lib=crypt32");
- println!("cargo:rustc-link-lib=gdi32");
- println!("cargo:rustc-link-lib=user32");
- })
- .is_ok()
-}
--- a/libssh2/src/userauth.c
+++ b/libssh2/src/userauth.c
@@ -80,6 +80,12 @@
memset(&session->userauth_list_packet_requirev_state, 0,
sizeof(session->userauth_list_packet_requirev_state));
+ if(username_len > UINT32_MAX - 27) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "username_len out of bounds");
+ return NULL;
+ }
+
session->userauth_list_data_len = username_len + 27;
s = session->userauth_list_data =
@@ -307,6 +313,11 @@
* 40 = packet_type(1) + username_len(4) + service_len(4) +
* service(14)"ssh-connection" + method_len(4) + method(8)"password" +
* chgpwdbool(1) + password_len(4) */
+ if(username_len > UINT32_MAX - 40) {
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "username_len out of bounds");
+ }
+
session->userauth_pswd_data_len = username_len + 40;
session->userauth_pswd_data0 =
@@ -447,7 +458,7 @@
}
/* basic data_len + newpw_len(4) */
- if(username_len + password_len + 44 <= UINT_MAX) {
+ if(username_len <= UINT32_MAX - password_len - 44) {
session->userauth_pswd_data_len =
username_len + password_len + 44;
s = session->userauth_pswd_data =
--- a/libssh2/src/sftp.c
+++ b/libssh2/src/sftp.c
@@ -1279,6 +1279,7 @@
"got HANDLE FXOK"));
LIBSSH2_FREE(session, data);
+ data = NULL;
/* silly situation, but check for a HANDLE */
rc = sftp_packet_require(sftp, SSH_FXP_HANDLE,
--- a/libssh2/src/openssl.c
+++ b/libssh2/src/openssl.c
@@ -1042,13 +1042,15 @@
const int aadlen = (is_aesgcm && IS_FIRST(firstlast)) ? 4 : 0;
/* size of AT, if present */
const int authenticationtag = IS_LAST(firstlast) ? authlen : 0;
- /* length to encrypt */
- const int cryptlen = (unsigned int)blocksize - aadlen - authenticationtag;
+ unsigned int cryptlen; /* length to encrypt */
(void)algo;
- assert(blocksize <= sizeof(buf));
- assert(cryptlen >= 0);
+ if(blocksize > sizeof(buf) ||
+ blocksize < (size_t)(aadlen + authenticationtag))
+ return 1;
+
+ cryptlen = (unsigned int)blocksize - aadlen - authenticationtag;
#if LIBSSH2_AES_GCM
/* First block */
--- a/libssh2/src/transport.c
+++ b/libssh2/src/transport.c
@@ -242,6 +242,13 @@
unsigned char *decrypt_buffer;
int blocksize = session->remote.crypt->blocksize;
+ if(p->total_num < mac_len + 4 + (size_t)blocksize) {
+ LIBSSH2_FREE(session, p->payload);
+ p->payload = NULL;
+ return LIBSSH2_ERROR_DECRYPT;
+ }
+ decrypt_size = (ssize_t)(p->total_num - mac_len - 4);
+
rc = decrypt(session, p->payload + 4,
first_block, blocksize, FIRST_BLOCK);
if(rc) {
@@ -249,7 +256,6 @@
}
/* we need buffer for decrypt */
- decrypt_size = p->total_num - mac_len - 4;
decrypt_buffer = LIBSSH2_ALLOC(session, decrypt_size);
if(!decrypt_buffer) {
return LIBSSH2_ERROR_ALLOC;
--- a/libssh2/src/publickey.c
+++ b/libssh2/src/publickey.c
@@ -972,6 +972,8 @@
goto err_exit;
}
list = newlist;
+ memset(&list[keys], 0,
+ (max_keys - keys + 1) * sizeof(list[keys]));
}
if(pkey->version == 1) {
unsigned long comment_len;
@@ -988,6 +990,13 @@
}
if(comment_len) {
+ if(pkey->listFetch_s + comment_len >
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
list[keys].num_attrs = 1;
list[keys].attrs =
LIBSSH2_ALLOC(session,
@@ -1114,6 +1123,11 @@
}
if(list[keys].num_attrs) {
+ if(list[keys].num_attrs > 1024) {
+ _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Too many publickey attributes");
+ goto err_exit;
+ }
list[keys].attrs =
LIBSSH2_ALLOC(session,
list[keys].num_attrs *
@@ -1188,10 +1202,9 @@
}
/* To be FREEd in libssh2_publickey_list_free() */
list[keys].packet = pkey->listFetch_data;
+ pkey->listFetch_data = NULL;
+
keys++;
-
- list[keys].packet = NULL; /* Terminate the list */
- pkey->listFetch_data = NULL;
break;
default:
/* Unknown/Unexpected */
@@ -1230,11 +1243,13 @@
session = pkey->channel->session;
- while(p->packet) {
+ while(p->attrs || p->packet) {
if(p->attrs) {
LIBSSH2_FREE(session, p->attrs);
}
- LIBSSH2_FREE(session, p->packet);
+ if(p->packet) {
+ LIBSSH2_FREE(session, p->packet);
+ }
p++;
}
+56
View File
@@ -0,0 +1,56 @@
[
{
"name": "7598-256d04b60d80.patch",
"url": "https://github.com/libssh2/libssh2/commit/256d04b60d80bf1190e96b0ad1e91b2174d744b1.patch",
"commit": "256d04b60d80bf1190e96b0ad1e91b2174d744b1",
"sha256": "fd4775228fe04a11532043355099fe2643993aae4fb63576620b1941f9a80645"
},
{
"name": "66032-5e4776146552.patch",
"url": "https://github.com/libssh2/libssh2/commit/5e4776146552d898b9c0e1b313cd093fa8dc92d0.patch",
"commit": "5e4776146552d898b9c0e1b313cd093fa8dc92d0",
"sha256": "a70ff256ce24fc5e4144fcc4409a5874c397e1f9e1b1d437120e4be3f5fc051d"
},
{
"name": "66033-a2ed82d40964.patch",
"url": "https://github.com/libssh2/libssh2/commit/a2ed82d40964bbc0d64cd717aa0a5a892117d2e6.patch",
"commit": "a2ed82d40964bbc0d64cd717aa0a5a892117d2e6",
"sha256": "f59ff6d6575ec8f17af41825aac4dc6c8ddcdd6909dc6ae3a535fa13dba8a290"
},
{
"name": "66034-a13bb6c773f0.patch",
"url": "https://github.com/libssh2/libssh2/commit/a13bb6c773f0d55ad1628cede57e99803cd898d9.patch",
"commit": "a13bb6c773f0d55ad1628cede57e99803cd898d9",
"sha256": "896ff800992af462519f0b2d2f47a84f5096728f27c9faf36b1676eaa9c6620f"
},
{
"name": "66035-42e33d81577e.patch",
"url": "https://github.com/libssh2/libssh2/commit/42e33d81577ed4b95d4b4f6f845e5ee8efe5eeb4.patch",
"commit": "42e33d81577ed4b95d4b4f6f845e5ee8efe5eeb4",
"sha256": "0422c11e22df21dc8154fa0aeb812157d0798d990bd9ab2cd05844dc21653693"
},
{
"name": "58050-34497525929b.patch",
"url": "https://github.com/libssh2/libssh2/commit/34497525929b9a47f03dfb81887ac896202b7e12.patch",
"commit": "34497525929b9a47f03dfb81887ac896202b7e12",
"sha256": "9cb033e07004a755675e2095eb7f05ebeaafa70b0920a1fdb8f6ad23498047d9"
},
{
"name": "58051-a9758da45a52.patch",
"url": "https://github.com/libssh2/libssh2/commit/a9758da45a52bc8c630ec9493804d0c6ea30b24a.patch",
"commit": "a9758da45a52bc8c630ec9493804d0c6ea30b24a",
"sha256": "0aee020bbc88acd2a3d828f1c85861f9f5a36204c07543cddc0abd68d4b5ed0a"
},
{
"name": "publickey-leak-c2f1a3a21b4c.patch",
"url": "https://github.com/libssh2/libssh2/commit/c2f1a3a21b4c922cbcf50bf7c009b740141a1e7a.patch",
"commit": "c2f1a3a21b4c922cbcf50bf7c009b740141a1e7a",
"sha256": "8d0e86562978b93406b85531e1f391d2f03c0387c057fb8b3a6e14b228aabdb5"
},
{
"name": "publickey-terminator-ad16eb112f4a.patch",
"url": "https://github.com/libssh2/libssh2/commit/ad16eb112f4a9591acdf5f6f2981328613e6b626.patch",
"commit": "ad16eb112f4a9591acdf5f6f2981328613e6b626",
"sha256": "6464c70d8d34249e5d0a0969136f35c1a2a0e3d0424c2889c5b8e6fbe874f232"
}
]
@@ -0,0 +1,35 @@
From 34497525929b9a47f03dfb81887ac896202b7e12 Mon Sep 17 00:00:00 2001
From: Viktor Szakats <commit@vsz.me>
Date: Sun, 28 Jun 2026 02:12:52 +0200
Subject: [PATCH] publickey: fix potential multiplication overflow in 32-bit
`libssh2_publickey_list_fetch()`
Cap list size at 1024 elements.
Reported-and-initial-patch-by: Mateusz Gierblinski
Reported-and-initial-patch-by: Behzod Abdullayev
Reported-by: Sharique Raza
Follow-up to e15f5d97a04cc676ce117dd324fef85b046207a9
Closes #2128
---
src/publickey.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/publickey.c b/src/publickey.c
index a0d3100005..3a6dd116d2 100644
--- a/src/publickey.c
+++ b/src/publickey.c
@@ -1048,6 +1048,11 @@ int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey,
}
if(list[keys].num_attrs) {
+ if(list[keys].num_attrs > 1024) {
+ ssh2_err(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Too many publickey attributes");
+ goto err_exit;
+ }
list[keys].attrs =
SSH2_ALLOC(session,
list[keys].num_attrs *
@@ -0,0 +1,28 @@
From a9758da45a52bc8c630ec9493804d0c6ea30b24a Mon Sep 17 00:00:00 2001
From: Viktor Szakats <vszakats@users.noreply.github.com>
Date: Mon, 29 Jun 2026 19:12:21 +0200
Subject: [PATCH] publickey: fix potential arbitrary free in
`libssh2_publickey_list_fetch()` (#2127)
Due to uninitialized list entry.
Reported-and-patch-by: Behzod Abdullayev
Reported-by: Sharique Raza
Follow-up to e15f5d97a04cc676ce117dd324fef85b046207a9
---
src/publickey.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/publickey.c b/src/publickey.c
index b7f384310e..a0d3100005 100644
--- a/src/publickey.c
+++ b/src/publickey.c
@@ -905,6 +905,7 @@ int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey,
goto err_exit;
}
list = newlist;
+ memset(&list[keys], 0, sizeof(list[keys]));
}
if(pkey->version == 1) {
unsigned long comment_len;
@@ -0,0 +1,25 @@
From 5e4776146552d898b9c0e1b313cd093fa8dc92d0 Mon Sep 17 00:00:00 2001
From: Will Cosgrove <will@panic.com>
Date: Thu, 2 Jul 2026 11:00:23 -0700
Subject: [PATCH] Prevent dangling pointer by nullifying data (#2180)
Set data to NULL after freeing it to avoid dangling pointer. fixes
GHSA-px3w-7g75-hg7w.
Credit: VladimirEliTokarev
---
src/sftp.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/sftp.c b/src/sftp.c
index ec3a8ae18a..842eb6ea51 100644
--- a/src/sftp.c
+++ b/src/sftp.c
@@ -1259,6 +1259,7 @@ static LIBSSH2_SFTP_HANDLE *sftp_open(LIBSSH2_SFTP *sftp,
ssh2_deb((session, LIBSSH2_TRACE_SFTP, "got HANDLE FXOK"));
SSH2_FREE(session, data);
+ data = NULL;
/* silly situation, but check for a HANDLE */
rc = sftp_packet_require(sftp, SSH_FXP_HANDLE,
@@ -0,0 +1,41 @@
From a2ed82d40964bbc0d64cd717aa0a5a892117d2e6 Mon Sep 17 00:00:00 2001
From: Viktor Szakats <commit@vsz.me>
Date: Thu, 23 Jul 2026 10:32:04 +0200
Subject: [PATCH] openssl: fix potential OOB read/write with AES-GCM in
`ssh2_cipher_crypt()`
By applying two bounds checks to non-debug builds.
Reported-by: Vladimir Eli Tokarev
Fixes GHSA-c4f7-cvfc-33j7
Follow-up to 3c953c05d67eb1ebcfd3316f279f12c4b1d600b4 #797
Closes #2401
---
src/openssl.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/openssl.c b/src/openssl.c
index 6a38b55b91..31ef6db371 100644
--- a/src/openssl.c
+++ b/src/openssl.c
@@ -945,13 +945,15 @@ int ssh2_cipher_crypt(ssh2_cipher_ctx *ctx, SSH2_CIPHER_T(algo), int encrypt,
const int aadlen = (is_aesgcm && IS_FIRST(firstlast)) ? 4 : 0;
/* size of AT, if present */
const int authenticationtag = IS_LAST(firstlast) ? authlen : 0;
- /* length to encrypt */
- const int cryptlen = (unsigned int)blocksize - aadlen - authenticationtag;
+ unsigned int cryptlen; /* length to encrypt */
(void)algo;
- assert(blocksize <= sizeof(buf));
- assert(cryptlen >= 0);
+ if(blocksize > sizeof(buf) ||
+ blocksize < (size_t)(aadlen + authenticationtag))
+ return 1;
+
+ cryptlen = (unsigned int)blocksize - aadlen - authenticationtag;
#if LIBSSH2_AES_GCM
/* First block */
@@ -0,0 +1,32 @@
From a13bb6c773f0d55ad1628cede57e99803cd898d9 Mon Sep 17 00:00:00 2001
From: Viktor Szakats <commit@vsz.me>
Date: Sat, 4 Jul 2026 11:19:49 +0200
Subject: [PATCH] publickey: fix potential OOB read in
`libssh2_publickey_list_fetch()`
Reported-by: Vladimir Eli Tokarev
Fixes GHSA-w6g9-cpfp-22gc
Closes #2202
---
src/publickey.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/publickey.c b/src/publickey.c
index 3254ad91e7..ec263c2287 100644
--- a/src/publickey.c
+++ b/src/publickey.c
@@ -929,6 +929,13 @@ int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey,
}
if(comment_len) {
+ if(pkey->listFetch_s + comment_len >
+ pkey->listFetch_data + pkey->listFetch_data_len) {
+ ssh2_err(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
+ "ListFetch data too short");
+ goto err_exit;
+ }
+
list[keys].num_attrs = 1;
list[keys].attrs =
SSH2_ALLOC(session,
@@ -0,0 +1,38 @@
From 42e33d81577ed4b95d4b4f6f845e5ee8efe5eeb4 Mon Sep 17 00:00:00 2001
From: Viktor Szakats <commit@vsz.me>
Date: Fri, 3 Jul 2026 18:22:55 +0200
Subject: [PATCH] transport: fix potential heap overflow on ETM decrypt
Reported-by: Vladimir Eli Tokarev
Fixes GHSA-6c79-444r-wx26
Closes #2198
---
src/transport.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/transport.c b/src/transport.c
index 6f05a0fdd4..94c66745c2 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -237,6 +237,12 @@ static int transport_fullpacket(LIBSSH2_SESSION *session,
unsigned char *decrypt_buffer;
int blocksize = session->remote.crypt->blocksize;
+ if(p->total_num < mac_len + 4 + (size_t)blocksize) {
+ SSH2_SAFEFREE(session, p->payload);
+ return LIBSSH2_ERROR_DECRYPT;
+ }
+ decrypt_size = (ssize_t)(p->total_num - mac_len - 4);
+
first_block[0] = 0;
rc = transport_decrypt(session, p->payload + 4,
@@ -245,7 +251,6 @@ static int transport_fullpacket(LIBSSH2_SESSION *session,
return rc;
/* we need buffer for decrypt */
- decrypt_size = p->total_num - mac_len - 4;
decrypt_buffer = SSH2_ALLOC(session, decrypt_size);
if(!decrypt_buffer)
return LIBSSH2_ERROR_ALLOC;
@@ -0,0 +1,52 @@
From 256d04b60d80bf1190e96b0ad1e91b2174d744b1 Mon Sep 17 00:00:00 2001
From: Will Cosgrove <will@panic.com>
Date: Mon, 13 Apr 2026 11:18:25 -0700
Subject: [PATCH] userauth.c: username_len bounds checking (#1858)
Return errors when username_len will exceed bounds, fix existing bounds
check.
Credit:
[dapickle](https://github.com/dapickle)
---
src/userauth.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/userauth.c b/src/userauth.c
index f8e02651c4..43d9ab9b9d 100644
--- a/src/userauth.c
+++ b/src/userauth.c
@@ -80,6 +80,12 @@ static char *userauth_list(LIBSSH2_SESSION *session, const char *username,
memset(&session->userauth_list_packet_requirev_state, 0,
sizeof(session->userauth_list_packet_requirev_state));
+ if(username_len > UINT32_MAX - 27) {
+ _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "username_len out of bounds");
+ return NULL;
+ }
+
session->userauth_list_data_len = username_len + 27;
if(session->userauth_list_data) {
@@ -316,6 +322,11 @@ userauth_password(LIBSSH2_SESSION *session,
* 40 = packet_type(1) + username_len(4) + service_len(4) +
* service(14)"ssh-connection" + method_len(4) + method(8)"password" +
* chgpwdbool(1) + password_len(4) */
+ if(username_len > UINT32_MAX - 40) {
+ return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
+ "username_len out of bounds");
+ }
+
session->userauth_pswd_data_len = username_len + 40;
session->userauth_pswd_data0 =
@@ -456,7 +467,7 @@ userauth_password(LIBSSH2_SESSION *session,
}
/* basic data_len + newpw_len(4) */
- if(username_len + password_len + 44 <= UINT_MAX) {
+ if(username_len <= UINT32_MAX - password_len - 44) {
session->userauth_pswd_data_len =
username_len + password_len + 44;
s = session->userauth_pswd_data =
@@ -0,0 +1,61 @@
From c2f1a3a21b4c922cbcf50bf7c009b740141a1e7a Mon Sep 17 00:00:00 2001
From: Viktor Szakats <commit@vsz.me>
Date: Tue, 21 Jul 2026 17:51:42 +0200
Subject: [PATCH] publickey: fix `attr` memleak on error in
`libssh2_publickey_list_fetch()`
In case of error, the last touched list entry could have the `attr`
field assigned to an allocated buffer, while still having NULL `packet`.
This made the destructore skip this entry and leak `attr`.
Fix by initializing all new list entries to zero and freeing entries
with either `attr` or `packet` set, up to entry where both are NULL.
Reported-by: hyunjiHong on github
Fixes #2368
Closes #2380
---
src/publickey.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/publickey.c b/src/publickey.c
index 43b8123e62..7db71a4249 100644
--- a/src/publickey.c
+++ b/src/publickey.c
@@ -941,7 +941,7 @@ int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey,
goto err_exit;
}
list = newlist;
- memset(&list[keys], 0, sizeof(list[keys]));
+ memset(&list[keys], 0, (max_keys - keys) * sizeof(list[keys]));
}
if(pkey->version == 1) {
unsigned long comment_len;
@@ -1180,10 +1180,9 @@ int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey,
}
/* To be FREEd in libssh2_publickey_list_free() */
list[keys].packet = pkey->listFetch_data;
- keys++;
-
- list[keys].packet = NULL; /* Terminate the list */
pkey->listFetch_data = NULL;
+
+ keys++;
break;
default:
/* Unknown/Unexpected */
@@ -1217,10 +1216,11 @@ void libssh2_publickey_list_free(LIBSSH2_PUBLICKEY *pkey,
session = pkey->channel->session;
- while(p->packet) {
+ while(p->attrs || p->packet) {
if(p->attrs)
SSH2_FREE(session, p->attrs);
- SSH2_FREE(session, p->packet);
+ if(p->packet)
+ SSH2_FREE(session, p->packet);
p++;
}
@@ -0,0 +1,32 @@
From ad16eb112f4a9591acdf5f6f2981328613e6b626 Mon Sep 17 00:00:00 2001
From: "RELAUNCH DEPT." <215028820+ceodaradigu@users.noreply.github.com>
Date: Thu, 3 Sep 2026 15:51:41 +0200
Subject: [PATCH] publickey: initialize the list terminator
The allocation reserves `max_keys + 1` entries, while the initialization
after growth currently covers only `max_keys - keys` entries. Include
the additional reserved entry in that initialization so the list
terminator is initialized at exact capacity boundaries.
Follow-up to c2f1a3a21b4c922cbcf50bf7c009b740141a1e7a #2380
Ref: https://github.com/libssh2/libssh2/pull/2380#pullrequestreview-4746725158
Closes #2579
---
src/publickey.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/publickey.c b/src/publickey.c
index d49ec48d38..36a60c141a 100644
--- a/src/publickey.c
+++ b/src/publickey.c
@@ -941,7 +941,8 @@ int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey,
goto err_exit;
}
list = newlist;
- memset(&list[keys], 0, (max_keys - keys) * sizeof(list[keys]));
+ memset(&list[keys], 0,
+ (max_keys - keys + 1) * sizeof(list[keys]));
}
if(pkey->version == 1) {
unsigned long comment_len;