Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ VALUE rb_obj_as_string_result(VALUE str, VALUE obj);
VALUE rb_str_opt_plus(VALUE x, VALUE y);
VALUE rb_str_new_owned(char *ptr, long len, long capa, int encindex);
VALUE rb_str_concat_literals(size_t num, const VALUE *strary);
VALUE rb_str_format_ary(int argc, const VALUE *argv, VALUE fmt, VALUE ary);
VALUE rb_str_eql(VALUE str1, VALUE str2);
VALUE rb_id_quote_unprintable(ID);
VALUE rb_sym_proc_call(ID mid, int argc, const VALUE *argv, int kw_splat, VALUE passed_proc);
Expand Down
24 changes: 23 additions & 1 deletion sprintf.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,26 @@ expand_result(VALUE result, long bsiz, long blen, long l)
blen += (l);\
} while (0)

static void
format_args_check(VALUE ary, int argc, const VALUE *argv)
{
if (!RTEST(ary)) return;
if (RARRAY_LEN(ary) != argc || RARRAY_CONST_PTR(ary) != argv) {
rb_raise(rb_eRuntimeError, "array modified during formatting");
}
}

#define GETARG() (!UNDEF_P(nextvalue) ? nextvalue : \
GETNEXTARG())

#define GETNEXTARG() ( \
check_next_arg(posarg, nextarg), \
(RTEST(ary) ? (format_args_check(ary, argc0, argv0), 0) : 0), \
(posarg = nextarg++, GETNTHARG(posarg)))

#define GETPOSARG(n) ( \
check_pos_arg(posarg, (n)), \
(RTEST(ary) ? (format_args_check(ary, argc0, argv0), 0) : 0), \
(posarg = -1, GETNTHARG(n)))

#define GETNTHARG(nth) \
Expand Down Expand Up @@ -224,7 +235,7 @@ rb_f_sprintf(int argc, const VALUE *argv)
}

VALUE
rb_str_format(int argc, const VALUE *argv, VALUE fmt)
rb_str_format_ary(int argc, const VALUE *argv, VALUE fmt, VALUE ary)
{
enum {default_float_precision = 6};
rb_encoding *enc;
Expand Down Expand Up @@ -268,6 +279,10 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
ENC_CODERANGE_BROKEN : (coderange = cr))); \
} \
} while (0)

const int argc0 = argc;
const VALUE *argv0 = argv;

++argc;
--argv;
StringValue(fmt);
Expand Down Expand Up @@ -945,6 +960,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
rb_str_tmp_frozen_release(orig, fmt);
/* XXX - We cannot validate the number of arguments if (digit)$ style used.
*/
format_args_check(ary, argc0, argv0);
if (posarg >= 0 && nextarg < argc && !(argc == 2 && RB_TYPE_P(argv[1], T_HASH))) {
const char *mesg = "too many arguments for format string";
if (RTEST(ruby_verbose)) rb_warn("%s", mesg);
Expand All @@ -954,6 +970,12 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt)
return result;
}

VALUE
rb_str_format(int argc, const VALUE *argv, VALUE fmt)
{
return rb_str_format_ary(argc, argv, fmt, Qfalse);
}

static char *
fmt_setup(char *buf, size_t size, int c, int flags, int width, int prec)
{
Expand Down
2 changes: 1 addition & 1 deletion string.c
Original file line number Diff line number Diff line change
Expand Up @@ -2695,7 +2695,7 @@ rb_str_format_m(VALUE str, VALUE arg)
VALUE tmp = rb_check_array_type(arg);

if (!NIL_P(tmp)) {
VALUE result = rb_str_format(RARRAY_LENINT(tmp), RARRAY_CONST_PTR(tmp), str);
VALUE result = rb_str_format_ary(RARRAY_LENINT(tmp), RARRAY_CONST_PTR(tmp), str, tmp);
RB_GC_GUARD(tmp);
return result;
}
Expand Down
14 changes: 14 additions & 0 deletions test/ruby/test_sprintf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,20 @@ def test_invalid
end
end

def test_modify_argument_array
count = 1_000
obj = Object.new
ary = [obj, *Array.new(count - 1) { "x" }]
obj.define_singleton_method(:to_s) do
ary.replace([])
"X"
end
str = "%s" * count
assert_raise_with_message(RuntimeError, /array modified during formatting/) do
str % ary
end
end

def test_float
assert_equal("36893488147419111424",
sprintf("%20.0f", 36893488147419107329.0))
Expand Down
15 changes: 13 additions & 2 deletions win32/Makefile.sub
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,17 @@ CPPFLAGS = $(DEFS) $(ARCHDEFS) $(CPPFLAGS)
CPPFLAGS = -DDISABLE_RUBYGEMS $(CPPFLAGS)
!endif

# https://learn.microsoft.com/en-us/cpp/build/reference/zc-preprocessor?view=msvc-170
!if $(MSC_VER) >= 1925
# maybe >= 1926?
# https://devblogs.microsoft.com/cppblog/announcing-full-support-for-a-c-c-conformant-preprocessor-in-msvc/
CPPFLAGS = -Zc:preprocessor $(CPPFLAGS)
!elseif $(MSC_VER) >= 1915
CPPFLAGS = -experimental:preprocessor $(CPPFLAGS)
!else
! error C99 conformant preprocessor is required
!endif

POSTLINK =
DLDFLAGS = $(LDFLAGS) -dll
MAINLIBS = $(LIBS)
Expand Down Expand Up @@ -1323,10 +1334,10 @@ modular-gc-precheck:

{$(srcdir)/coroutine/win32}.asm{coroutine/win32}.obj:
$(ECHO) assembling $(<:\=/)
$(Q) $(AS) $(ASFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(<:\=/)
$(Q) $(AS) $(ASFLAGS) $(COUTFLAG)$@ -c $(<:\=/)
{$(srcdir)/coroutine/win64}.asm{coroutine/win64}.obj:
$(ECHO) assembling $(<:\=/)
$(Q) $(AS) $(ASFLAGS) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -c $(<:\=/)
$(Q) $(AS) $(ASFLAGS) $(COUTFLAG)$@ -c $(<:\=/)
{$(srcdir)/coroutine/arm64}.asm{coroutine/arm64}.obj:
$(ECHO) assembling $(<:\=/)
$(Q) $(AS) $(ASFLAGS) -o $@ $(<:\=/)
Expand Down