ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
From: mame@ruby-lang.org
To: ruby-core@ruby-lang.org
Subject: [ruby-core:94833] [Ruby master Bug#16151] [PATCH] Fix a class of fstring related use-after-free
Date: Sun, 08 Sep 2019 00:45:56 +0000 (UTC)	[thread overview]
Message-ID: <redmine.journal-81453.20190908004555.6f813c8e4f3a7b65@ruby-lang.org> (raw)
In-Reply-To: redmine.issue-16151.20190907190949@ruby-lang.org

Issue #16151 has been updated by mame (Yusuke Endoh).


Hi @alanwu , thank you for taking time to this issue.

I may be wrong, but I think that the code path you pointed is benign itself.  It calls `str_replace_shared_without_enc` which may free the string buffer.   However, it does not free the buffer if it is STR_NOFREE.  I guess that we need to set STR_NOFREE flag somewhere, though I cannot understand at all what is STR_NOFREE.

Looks like `str_new_frozen` creates a broken shared pair.

https://github.com/ruby/ruby/blob/e9bc8b35c6c860e227627e3b0aab86b4f51c59af/string.c#L1303-L1312

It allocates `str`, copies the contents from `orig` to `str`, make the buffer shared, but does not set the STR_NOFREE flag to `str`.

The following patch fixes the two examples you showed.  But it would produce a new memory-leak issue, I guess.  @nobu, what do you think?

```
diff --git a/string.c b/string.c
index 05ce0ed8d6..d810f252e7 100644
--- a/string.c
+++ b/string.c
@@ -1305,7 +1305,7 @@ str_new_frozen(VALUE klass, VALUE orig)
            RSTRING(str)->as.heap.len = RSTRING_LEN(orig);
            RSTRING(str)->as.heap.ptr = RSTRING_PTR(orig);
            RSTRING(str)->as.heap.aux.capa = RSTRING(orig)->as.heap.aux.capa;
-           RBASIC(str)->flags |= RBASIC(orig)->flags & STR_NOFREE;
+           RBASIC(str)->flags |= STR_NOFREE;
            RBASIC(orig)->flags &= ~STR_NOFREE;
            STR_SET_SHARED(orig, str);
            if (klass == 0)
```

Honestly, fstring looks ill-designed to me.

----------------------------------------
Bug #16151: [PATCH] Fix a class of fstring related use-after-free
https://bugs.ruby-lang.org/issues/16151#change-81453

* Author: alanwu (Alan Wu)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
* ruby -v: ruby 2.7.0dev (2019-09-07T18:26:35Z master e9bc8b35c6) [x86_64-linux]
* Backport: 2.5: UNKNOWN, 2.6: UNKNOWN
----------------------------------------
Pull request: https://github.com/ruby/ruby/pull/2435

## The bug

Run the following against master(e9bc8b3) to observe use-after-free:

```ruby
-('a' * 30).force_encoding(Encoding::ASCII)
a = ('a' * 30).force_encoding(Encoding::ASCII).taint

t = Thread.new{}
t.name = a
eval('', binding, t.name)
p a
```

```ruby
-('a' * 30).force_encoding(Encoding::ASCII)
a = ('a' * 30).force_encoding(Encoding::ASCII).taint

require 'ripper'
ripper = Ripper.new("", a)
eval('', binding, ripper.filename)
p a
```

There may be other cases in the standard library or in the wild.

## Background

When a string has both `STR_NOEMBED` and `STR_SHARED` set, it relies on a
different string for its buffer. I will refer to strings that are depended upon
as "shared roots". Shared roots are frozen and have the `STR_SHARED` unset.
This is a bit unintuitive to me. A name for `STR_SHARED` that makes more sense
to me would be `STR_BUFFER_ELSEWHERE`.

## What went wrong

It is not safe to free the buffer of a shared root while it has dependants. The
root and its dependants use the same buffer. As such, it is only safe to free
the shared buffer when all users are unreachable on the heap.

## The Fix

`rb_fstring` has a code path that frees and replaces the buffer of its input.
Using this code path on the shared root of dependant strings sets up
use-after-free. This patch removes the problematic code path as no tests
require said buffer replacement functionality. Additionally, there has been
three other issues that steam from this particular code path. See #15926,
#15916 and #16136

---

I used @mame's commit in #16136 as the starting point for this investigation.
Thank you!



-- 
https://bugs.ruby-lang.org/

  parent reply	other threads:[~2019-09-08  0:46 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <redmine.issue-16151.20190907190949@ruby-lang.org>
2019-09-07 19:09 ` [ruby-core:94831] [Ruby master Bug#16151] [PATCH] Fix a class of fstring related use-after-free XrXr
2019-09-08  0:45 ` mame [this message]
2019-09-08  0:59 ` [ruby-core:94834] " mame
2019-09-08  3:36 ` [ruby-core:94837] " XrXr
2019-09-08  7:01 ` [ruby-core:94838] " nobu
2019-09-08 23:02 ` [ruby-core:94848] " XrXr
2019-09-18  7:28 ` [ruby-core:94952] " mame
2019-09-18 15:13 ` [ruby-core:94960] " XrXr
2019-09-18 22:57 ` [ruby-core:94962] " mame
2019-09-19 20:31 ` [ruby-core:94993] " XrXr
2019-09-23  0:25 ` [ruby-core:95040] " XrXr
2019-09-26 11:59 ` [ruby-core:95103] " nagachika00
2019-09-27 11:23 ` [ruby-core:95132] " nagachika00

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.ruby-lang.org/en/community/mailing-lists/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=redmine.journal-81453.20190908004555.6f813c8e4f3a7b65@ruby-lang.org \
    --to=ruby-core@ruby-lang.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).