Compare commits
No commits in common. "c8" and "c8-beta" have entirely different histories.
@ -1,52 +0,0 @@
|
||||
From d4ec4ab3aaf1fa736a6195c2d1317450dc1f2c4b Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Tina=20M=C3=BCller?= <cpan2@tinita.de>
|
||||
Date: Tue, 2 Oct 2018 22:56:00 +0200
|
||||
Subject: [PATCH] Fix memory corruption error
|
||||
|
||||
Currently the following examples abort the program:
|
||||
|
||||
"- &anchor1 &anchor2 x"
|
||||
double free or corruption (fasttop)
|
||||
|
||||
"- &anchor1 &anchor2 x"
|
||||
double free or corruption (fasttop)
|
||||
|
||||
"x: &anchor1 &anchor2 x"
|
||||
malloc(): memory corruption (fast)
|
||||
|
||||
"key1: &a value1
|
||||
&b *a : value2"
|
||||
double free or corruption (fasttop)
|
||||
|
||||
This fix will prevent the abort and falsely accept invalid YAML.
|
||||
Instead the code should return an error/throw an exception, but I don't
|
||||
know how to do this here.
|
||||
So this is just the less optimal solution, avoiding a program abort.
|
||||
---
|
||||
handler.c | 10 ++++++++++
|
||||
1 file changed, 10 insertions(+)
|
||||
|
||||
diff --git a/handler.c b/handler.c
|
||||
index d9cf8ec..5de4359 100644
|
||||
--- a/handler.c
|
||||
+++ b/handler.c
|
||||
@@ -32,6 +32,16 @@ syck_hdlr_add_anchor( SyckParser *p, char *a, SyckNode *n )
|
||||
{
|
||||
SyckNode *ntmp = NULL;
|
||||
|
||||
+ if (n->anchor != NULL) {
|
||||
+ /*
|
||||
+ * Note: An error should be returned here. But this is better than
|
||||
+ * not checking at all because it will abort the program with a
|
||||
+ * memory corruption error.
|
||||
+ * Happens if you have two anchors after each other or an anchor
|
||||
+ * before an alias
|
||||
+ * */
|
||||
+ return n;
|
||||
+ }
|
||||
n->anchor = a;
|
||||
if ( p->bad_anchors != NULL )
|
||||
{
|
||||
--
|
||||
2.53.0
|
||||
|
||||
@ -1,268 +0,0 @@
|
||||
From e8844a31c8cf0052914b198fc784ed4e6b8ae69e Mon Sep 17 00:00:00 2001
|
||||
From: Toddr Bot <toddbot@rinaldo.us>
|
||||
Date: Sat, 14 Mar 2026 07:18:00 +0000
|
||||
Subject: [PATCH] fix: address all 4 C-layer audit findings from issue #67
|
||||
|
||||
- HIGH: Fix heap buffer overflow in emitter tag buffer. The 512-byte
|
||||
fixed allocation overflowed via strcat(tag, ref) with long class names.
|
||||
Now tracks buffer size and grows dynamically with Renew() when needed.
|
||||
|
||||
- MEDIUM: Fix base64 decoder reading past buffer end on trailing
|
||||
newlines. Added s < send guard to the inner whitespace-skip loop.
|
||||
|
||||
- MEDIUM: Replace strtok(id, "/:") with savepv copy + strtok at all 6
|
||||
call sites in the parser handler. strtok mutated n->type_id in place,
|
||||
corrupting shared node data. Each site now operates on a local copy
|
||||
that is freed after use.
|
||||
|
||||
- LOW: Fix memory leak in syck_hdlr_add_anchor when a node already has
|
||||
an anchor. The incoming anchor string 'a' was leaked on early return.
|
||||
|
||||
Closes #67
|
||||
|
||||
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
||||
---
|
||||
emitter.c | 3 ++-
|
||||
handler.c | 1 +
|
||||
perl_common.h | 1 +
|
||||
perl_syck.h | 36 +++++++++++++++++++++++++++++-------
|
||||
t/tag-overflow.t | 44 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
5 files changed, 77 insertions(+), 8 deletions(-)
|
||||
create mode 100644 t/tag-overflow.t
|
||||
|
||||
diff --git a/emitter.c b/emitter.c
|
||||
index cd8150b..da3c91b 100644
|
||||
--- a/emitter.c
|
||||
+++ b/emitter.c
|
||||
@@ -80,7 +80,8 @@ syck_base64dec( char *s, long len, long *out_len )
|
||||
}
|
||||
}
|
||||
while (s < send) {
|
||||
- while (s[0] == '\r' || s[0] == '\n') { s++; }
|
||||
+ while (s < send && (s[0] == '\r' || s[0] == '\n')) { s++; }
|
||||
+ if (s >= send) break;
|
||||
if ((a = b64_xtable[(int)s[0]]) == -1) break;
|
||||
if ((b = b64_xtable[(int)s[1]]) == -1) break;
|
||||
if ((c = b64_xtable[(int)s[2]]) == -1) break;
|
||||
diff --git a/handler.c b/handler.c
|
||||
index 5de4359..48341a9 100644
|
||||
--- a/handler.c
|
||||
+++ b/handler.c
|
||||
@@ -40,6 +40,7 @@ syck_hdlr_add_anchor( SyckParser *p, char *a, SyckNode *n )
|
||||
* Happens if you have two anchors after each other or an anchor
|
||||
* before an alias
|
||||
* */
|
||||
+ S_FREE(a);
|
||||
return n;
|
||||
}
|
||||
n->anchor = a;
|
||||
diff --git a/perl_common.h b/perl_common.h
|
||||
index 4512237..bc9924b 100644
|
||||
--- a/perl_common.h
|
||||
+++ b/perl_common.h
|
||||
@@ -37,6 +37,7 @@ struct emitter_xtra {
|
||||
PerlIO* outio;
|
||||
} out;
|
||||
char* tag;
|
||||
+ STRLEN tag_len;
|
||||
char dump_code;
|
||||
bool implicit_binary;
|
||||
int ioerror;
|
||||
diff --git a/perl_syck.h b/perl_syck.h
|
||||
index 14cd311..1b00bd6 100644
|
||||
--- a/perl_syck.h
|
||||
+++ b/perl_syck.h
|
||||
@@ -332,7 +332,8 @@ yaml_syck_parser_handler
|
||||
|
||||
} else if (strnEQ( n->data.str->ptr, REF_LITERAL, 1+REF_LITERAL_LENGTH)) {
|
||||
/* type tag in a scalar ref */
|
||||
- char *lang = strtok(id, "/:");
|
||||
+ char *id_copy = savepv(id);
|
||||
+ char *lang = strtok(id_copy, "/:");
|
||||
char *type = strtok(NULL, "");
|
||||
|
||||
if (lang == NULL || (strEQ(lang, "perl"))) {
|
||||
@@ -341,6 +342,7 @@ yaml_syck_parser_handler
|
||||
else {
|
||||
sv = newSVpv(form((type == NULL) ? "%s" : "%s::%s", lang, type), 0);
|
||||
}
|
||||
+ Safefree(id_copy);
|
||||
} else if ( strEQ( id, "perl/scalar" ) || strnEQ( id, "perl/scalar:", 12 ) ) {
|
||||
char *pkg = id + 12;
|
||||
|
||||
@@ -362,7 +364,8 @@ yaml_syck_parser_handler
|
||||
} else if ( (strEQ(id, "perl/regexp") || strnEQ( id, "perl/regexp:", 12 ) ) ) {
|
||||
dSP;
|
||||
SV *val = newSVpvn(n->data.str->ptr, n->data.str->len);
|
||||
- char *lang = strtok(id, "/:");
|
||||
+ char *id_copy = savepv(id);
|
||||
+ char *lang = strtok(id_copy, "/:");
|
||||
char *type = strtok(NULL, "");
|
||||
|
||||
ENTER;
|
||||
@@ -396,6 +399,7 @@ yaml_syck_parser_handler
|
||||
sv_bless(sv, gv_stashpv(form((type == NULL) ? "%s" : "%s::%s", lang, type), TRUE));
|
||||
}
|
||||
}
|
||||
+ Safefree(id_copy);
|
||||
#endif /* PERL_LOADMOD_NOIMPORT */
|
||||
#endif /* !YAML_IS_JSON */
|
||||
} else {
|
||||
@@ -427,7 +431,8 @@ yaml_syck_parser_handler
|
||||
|
||||
if (id) {
|
||||
/* bless it if necessary */
|
||||
- char *lang = strtok(id, "/:");
|
||||
+ char *id_copy = savepv(id);
|
||||
+ char *lang = strtok(id_copy, "/:");
|
||||
char *type = strtok(NULL, "");
|
||||
|
||||
if ( type != NULL ) {
|
||||
@@ -435,7 +440,7 @@ yaml_syck_parser_handler
|
||||
/* !perl/array:Foo::Bar blesses into Foo::Bar */
|
||||
type += 6;
|
||||
}
|
||||
-
|
||||
+
|
||||
/* FIXME deprecated - here compatibility with @Foo::Bar style blessing */
|
||||
while ( *type == '@' ) { type++; }
|
||||
}
|
||||
@@ -451,6 +456,7 @@ yaml_syck_parser_handler
|
||||
sv_bless(sv, gv_stashpv(form((type == NULL) ? "%s" : "%s::%s", lang, type), TRUE));
|
||||
}
|
||||
}
|
||||
+ Safefree(id_copy);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
@@ -480,7 +486,8 @@ yaml_syck_parser_handler
|
||||
}
|
||||
else {
|
||||
/* bless it if necessary */
|
||||
- char *lang = strtok(id, "/:");
|
||||
+ char *id_copy = savepv(id);
|
||||
+ char *lang = strtok(id_copy, "/:");
|
||||
char *type = strtok(NULL, "");
|
||||
|
||||
if ( type != NULL && strnEQ(type, "ref:", 4)) {
|
||||
@@ -496,6 +503,7 @@ yaml_syck_parser_handler
|
||||
else {
|
||||
sv_bless(sv, gv_stashpv(form((type == NULL) ? "%s" : "%s::%s", lang, type), TRUE));
|
||||
}
|
||||
+ Safefree(id_copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -527,7 +535,8 @@ yaml_syck_parser_handler
|
||||
}
|
||||
else {
|
||||
/* bless it if necessary */
|
||||
- char *lang = strtok(id, "/:");
|
||||
+ char *id_copy = savepv(id);
|
||||
+ char *lang = strtok(id_copy, "/:");
|
||||
char *type = strtok(NULL, "");
|
||||
|
||||
if ( type != NULL && strnEQ(type, "regexp:", 7)) {
|
||||
@@ -544,6 +553,7 @@ yaml_syck_parser_handler
|
||||
else {
|
||||
sv_bless(sv, gv_stashpv(form((type == NULL) ? "%s" : "%s::%s", lang, type), TRUE));
|
||||
}
|
||||
+ Safefree(id_copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -579,7 +589,8 @@ yaml_syck_parser_handler
|
||||
#ifndef YAML_IS_JSON
|
||||
if (id) {
|
||||
/* bless it if necessary */
|
||||
- char *lang = strtok(id, "/:");
|
||||
+ char *id_copy = savepv(id);
|
||||
+ char *lang = strtok(id_copy, "/:");
|
||||
char *type = strtok(NULL, "");
|
||||
|
||||
if ( type != NULL ) {
|
||||
@@ -602,6 +613,7 @@ yaml_syck_parser_handler
|
||||
sv_bless(sv, gv_stashpv(form((type == NULL) ? "%s" : "%s::%s", lang, type), TRUE));
|
||||
}
|
||||
}
|
||||
+ Safefree(id_copy);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -960,6 +972,15 @@ yaml_syck_emitter_handler
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+ {
|
||||
+ /* Grow tag buffer if ref won't fit (prevents heap overflow) */
|
||||
+ STRLEN need = strlen(tag) + strlen(ref) + 1;
|
||||
+ if (need > bonus->tag_len) {
|
||||
+ Renew(bonus->tag, need, char);
|
||||
+ bonus->tag_len = need;
|
||||
+ tag = bonus->tag;
|
||||
+ }
|
||||
+ }
|
||||
strcat(tag, ref);
|
||||
}
|
||||
#endif
|
||||
@@ -1284,6 +1305,7 @@ DumpYAMLImpl
|
||||
emitter->anchor_format = "%d";
|
||||
|
||||
New(801, bonus->tag, 512, char);
|
||||
+ bonus->tag_len = 512;
|
||||
*(bonus->tag) = '\0';
|
||||
bonus->dump_code = SvTRUE(use_code) || SvTRUE(dump_code);
|
||||
bonus->implicit_binary = SvTRUE(implicit_binary);
|
||||
diff --git a/t/tag-overflow.t b/t/tag-overflow.t
|
||||
new file mode 100644
|
||||
index 0000000..8570f13
|
||||
--- /dev/null
|
||||
+++ b/t/tag-overflow.t
|
||||
@@ -0,0 +1,44 @@
|
||||
+use FindBin;
|
||||
+BEGIN { push @INC, $FindBin::Bin }
|
||||
+
|
||||
+use TestYAML tests => 6;
|
||||
+
|
||||
+$YAML::Syck::LoadBlessed = 1;
|
||||
+
|
||||
+# Test that Dump handles objects with very long class names without crashing.
|
||||
+# This exercises the dynamic tag buffer growth added to prevent a heap overflow
|
||||
+# when class names exceed the initial 512-byte allocation.
|
||||
+
|
||||
+my $short_class = 'A' x 100;
|
||||
+my $long_class = 'B' x 600; # exceeds initial 512-byte tag buffer
|
||||
+my $huge_class = 'C' x 2000;
|
||||
+
|
||||
+# Short class name (fits in initial buffer)
|
||||
+{
|
||||
+ my $obj = bless {}, $short_class;
|
||||
+ my $yaml = Dump($obj);
|
||||
+ like($yaml, qr/!!perl\/hash:\Q$short_class\E/, "dump short class name ($short_class)");
|
||||
+
|
||||
+ my $rt = Load($yaml);
|
||||
+ is(ref($rt), $short_class, "roundtrip short class name");
|
||||
+}
|
||||
+
|
||||
+# Long class name (exceeds 512-byte buffer)
|
||||
+{
|
||||
+ my $obj = bless {}, $long_class;
|
||||
+ my $yaml = Dump($obj);
|
||||
+ like($yaml, qr/!!perl\/hash:\Q$long_class\E/, "dump long class name (600 chars)");
|
||||
+
|
||||
+ my $rt = Load($yaml);
|
||||
+ is(ref($rt), $long_class, "roundtrip long class name");
|
||||
+}
|
||||
+
|
||||
+# Huge class name
|
||||
+{
|
||||
+ my $obj = bless {}, $huge_class;
|
||||
+ my $yaml = Dump($obj);
|
||||
+ like($yaml, qr/!!perl\/hash:\Q$huge_class\E/, "dump huge class name (2000 chars)");
|
||||
+
|
||||
+ my $rt = Load($yaml);
|
||||
+ is(ref($rt), $huge_class, "roundtrip huge class name");
|
||||
+}
|
||||
--
|
||||
2.53.0
|
||||
|
||||
@ -7,15 +7,11 @@
|
||||
|
||||
Name: perl-YAML-Syck
|
||||
Version: 1.30
|
||||
Release: 6%{?dist}
|
||||
Release: 5%{?dist}
|
||||
Summary: Fast, lightweight YAML loader and dumper
|
||||
License: BSD and MIT
|
||||
URL: http://search.cpan.org/dist/YAML-Syck/
|
||||
Source0: http://www.cpan.org/authors/id/T/TO/TODDR/YAML-Syck-%{version}.tar.gz
|
||||
# Fix memory corruption error
|
||||
Patch0: YAML-Syck-1.33-Fix-memory-corruption-error.patch
|
||||
# Fix heap buffer overflow in the YAML emitter - CVE-2026-4177
|
||||
Patch1: YAML-Syck-1.37-Fix-CVE-2026-4177.patch
|
||||
BuildRequires: coreutils
|
||||
BuildRequires: findutils
|
||||
BuildRequires: gcc
|
||||
@ -62,8 +58,6 @@ structures to YAML strings, and the other way around.
|
||||
|
||||
%prep
|
||||
%setup -q -n YAML-Syck-%{version}
|
||||
%patch -P0 -p1
|
||||
%patch -P1 -p1
|
||||
|
||||
# Unbundle core and unused modules
|
||||
rm -rvf inc/{parent.pm,PerlIO.pm,Scalar/,Test/}
|
||||
@ -91,10 +85,6 @@ make test
|
||||
%{_mandir}/man3/YAML::Syck.3*
|
||||
|
||||
%changelog
|
||||
* Wed Mar 25 2026 Jitka Plesnikova <jplesnik@redhat.com> - 1.30-6
|
||||
- Resolves: RHEL-156475
|
||||
- Fix CVE-2026-4177
|
||||
|
||||
* Fri Feb 09 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.30-5
|
||||
- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user