diff --git a/src/core/pp.c b/src/core/pp.c index 4d01d34c..8aff7e44 100644 --- a/src/core/pp.c +++ b/src/core/pp.c @@ -198,8 +198,15 @@ void janet_description_b(JanetBuffer *buffer, Janet x) { janet_escape_string_b(buffer, janet_unwrap_string(x)); return; case JANET_BUFFER: - janet_escape_buffer_b(buffer, janet_unwrap_buffer(x)); - return; + { + JanetBuffer *b = janet_unwrap_buffer(x); + if (b == buffer) { + /* Ensures buffer won't resize while escaping */ + janet_buffer_ensure(b, 5 * b->count + 3, 1); + } + janet_escape_buffer_b(buffer, b); + return; + } case JANET_ABSTRACT: { void *p = janet_unwrap_abstract(x); const JanetAbstractType *at = janet_abstract_type(p); @@ -298,6 +305,7 @@ struct pretty { int depth; int indent; int flags; + int32_t bufstartlen; JanetTable seen; }; @@ -372,7 +380,13 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) { if (color && (S->flags & JANET_PRETTY_COLOR)) { janet_buffer_push_cstring(S->buffer, color); } - janet_description_b(S->buffer, x); + if (janet_checktype(x, JANET_BUFFER) && janet_unwrap_buffer(x) == S->buffer) { + janet_buffer_ensure(S->buffer, S->buffer->count + S->bufstartlen * 4 + 3, 1); + janet_buffer_push_u8(S->buffer, '@'); + janet_escape_string_impl(S->buffer, S->buffer->data, S->bufstartlen); + } else { + janet_description_b(S->buffer, x); + } if (color && (S->flags & JANET_PRETTY_COLOR)) { janet_buffer_push_cstring(S->buffer, "\x1B[0m"); } @@ -461,9 +475,7 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) { return; } -/* Helper for printing a janet value in a pretty form. Not meant to be used - * for serialization or anything like that. */ -JanetBuffer *janet_pretty(JanetBuffer *buffer, int depth, int flags, Janet x) { +static JanetBuffer *janet_pretty_(JanetBuffer *buffer, int depth, int flags, Janet x, int32_t startlen) { struct pretty S; if (NULL == buffer) { buffer = janet_buffer(0); @@ -472,12 +484,19 @@ JanetBuffer *janet_pretty(JanetBuffer *buffer, int depth, int flags, Janet x) { S.depth = depth; S.indent = 0; S.flags = flags; + S.bufstartlen = startlen; janet_table_init(&S.seen, 10); janet_pretty_one(&S, x, 0); janet_table_deinit(&S.seen); return S.buffer; } +/* Helper for printing a janet value in a pretty form. Not meant to be used + * for serialization or anything like that. */ +JanetBuffer *janet_pretty(JanetBuffer *buffer, int depth, int flags, Janet x) { + return janet_pretty_(buffer, depth, flags, x, buffer ? buffer->count : 0); +} + static const char *typestr(Janet x) { JanetType t = janet_type(x); return (t == JANET_ABSTRACT) @@ -643,6 +662,7 @@ void janet_buffer_format( size_t sfl = strlen(strfrmt); const char *strfrmt_end = strfrmt + sfl; int32_t arg = argstart; + int32_t startlen = b->count; while (strfrmt < strfrmt_end) { if (*strfrmt != '%') janet_buffer_push_u8(b, (uint8_t) * strfrmt++); @@ -711,7 +731,7 @@ void janet_buffer_format( int depth = atoi(precision); if (depth < 1) depth = 4; - janet_pretty(b, depth, (strfrmt[-1] == 'P') ? JANET_PRETTY_COLOR : 0, argv[arg]); + janet_pretty_(b, depth, (strfrmt[-1] == 'P') ? JANET_PRETTY_COLOR : 0, argv[arg], startlen); break; } default: { diff --git a/test/suite2.janet b/test/suite2.janet index 0f0fcecd..c08b9baa 100644 --- a/test/suite2.janet +++ b/test/suite2.janet @@ -46,14 +46,6 @@ (assert (= txs '[[-1 -1] [-1 0] [-1 1] [0 -1] [0 1] [1 -1] [1 0] [1 1]]) "nested seq") -# Buffer self blitting, check for use after free -(def buf1 @"1234567890") -(buffer/blit buf1 buf1 -1) -(buffer/blit buf1 buf1 -1) -(buffer/blit buf1 buf1 -1) -(buffer/blit buf1 buf1 -1) -(assert (= (string buf1) (string/repeat "1234567890" 16)) "buffer blit against self") - # Generators (def gen (generate [x :range [0 100] :when (pos? (% x 4))] x)) (var gencount 0) diff --git a/test/suite3.janet b/test/suite3.janet index c0707e4b..6fa3e9d9 100644 --- a/test/suite3.janet +++ b/test/suite3.janet @@ -159,6 +159,14 @@ (buffer/blit b2 "abcdefg" 5 6) (assert (= (string b2) "joytogjoyto") "buffer/blit 3") +# Buffer self blitting, check for use after free +(def buf1 @"1234567890") +(buffer/blit buf1 buf1 -1) +(buffer/blit buf1 buf1 -1) +(buffer/blit buf1 buf1 -1) +(buffer/blit buf1 buf1 -1) +(assert (= (string buf1) (string/repeat "1234567890" 16)) "buffer blit against self") + # Buffer push word (def b3 @"") @@ -179,6 +187,13 @@ (buffer/push-string b5 "456" @"789") (assert (= "123456789" (string b5)) "buffer/push-buffer 2") +# Check for bugs with printing self with buffer/format + +(def buftemp @"abcd") +(assert (= (string (buffer/format buftemp "---%p---" buftemp)) `abcd---@"abcd"---`) "buffer/format on self 1") +(def buftemp @"abcd") +(assert (= (string (buffer/format buftemp "---%p %p---" buftemp buftemp)) `abcd---@"abcd" @"abcd"---`) "buffer/format on self 2") + # Peg (defn check-match