From b32641a02048ca3db326789a1ffcf336d4e29910 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Wed, 14 Jan 2026 21:44:39 +0300 Subject: [PATCH 1/2] setlevels(): avoid crash on missing factor values --- NEWS.md | 2 ++ inst/tests/tests.Rraw | 3 +++ src/wrappers.c | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 3f2fd8df59..5d86a0f49e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -34,6 +34,8 @@ 7. Fixed compilation failure like "error: unknown type name 'siginfo_t'" in v1.18.0 in some strict environments, e.g., FreeBSD, where the header file declaring the POSIX function `waitid` does not transitively include the header file defining the `siginfo_t` type, [#7516](https://github.com/rdatatable/data.table/issues/7516). Thanks to @jszhao for the report and @aitap for the fix. +8. When fixing duplicate factor levels, `setattr()` no longer crashes upon encountering missing factor values, [#7595](https://github.com/Rdatatable/data.table/issues/7595). Thanks to @sindribaldur for the report and @aitap for the fix. + ### Notes 1. {data.table} now depends on R 3.5.0 (2018). diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index b5ef44c6b2..9afec9b5b9 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -22100,3 +22100,6 @@ test(2360.4, rowwiseDT(x =, expr =, 1, quote(a + b)), error = "Column 'expr' is type 'call'. Non-atomic, non-list objects must be wrapped in list\\(\\)") test(2360.5, rowwiseDT(x =, plist =, 1, as.pairlist(list(123))), error = "Column 'plist' is type 'pairlist'. Non-atomic, non-list objects must be wrapped in list\\(\\)") + +# setattr() must not fail with missing factor values when fixing duplicate levels, #7595 +test(2361, setattr(factor(c(1, NA), levels = 1), "levels", c("1", "1")), factor(c(1, NA))) diff --git a/src/wrappers.c b/src/wrappers.c index 2b26761bfd..8e940d0ca6 100644 --- a/src/wrappers.c +++ b/src/wrappers.c @@ -45,7 +45,7 @@ SEXP setlevels(SEXP x, SEXP levels, SEXP ulevels) { xchar = PROTECT(allocVector(STRSXP, nx)); int *ix = INTEGER(x); for (int i=0; i Date: Wed, 14 Jan 2026 22:23:38 +0300 Subject: [PATCH 2/2] setlevels(): also avoid crashing for indices < 1 --- inst/tests/tests.Rraw | 5 +++-- src/wrappers.c | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 9afec9b5b9..25a03c1fbf 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -22101,5 +22101,6 @@ test(2360.4, rowwiseDT(x =, expr =, 1, quote(a + b)), test(2360.5, rowwiseDT(x =, plist =, 1, as.pairlist(list(123))), error = "Column 'plist' is type 'pairlist'. Non-atomic, non-list objects must be wrapped in list\\(\\)") -# setattr() must not fail with missing factor values when fixing duplicate levels, #7595 -test(2361, setattr(factor(c(1, NA), levels = 1), "levels", c("1", "1")), factor(c(1, NA))) +# setattr() must not crash for out-of-bounds factor indices when fixing duplicate levels, #7595 +test(2361.1, setattr(factor(c(1, NA), levels = 1), "levels", c("1", "1")), factor(c(1, NA))) +test(2361.2, setattr(structure(c(-999L, 999L), class = "factor", levels = "a"), "levels", c("b", "b")), factor(c(NA, NA), levels = "b")) diff --git a/src/wrappers.c b/src/wrappers.c index 8e940d0ca6..fb6aa7f351 100644 --- a/src/wrappers.c +++ b/src/wrappers.c @@ -44,8 +44,11 @@ SEXP setlevels(SEXP x, SEXP levels, SEXP ulevels) { SEXP xchar, newx; xchar = PROTECT(allocVector(STRSXP, nx)); int *ix = INTEGER(x); - for (int i=0; i= 1 && ixi <= nlevels) ? STRING_ELT(levels, ix[i]-1) : NA_STRING); + } newx = PROTECT(chmatch(xchar, ulevels, NA_INTEGER)); int *inewx = INTEGER(newx); for (int i=0; i