Non-constant strings

Hello. Why does Ruby have non-constant strings? It seems there is a way
to bypass object encapsulation paradigm and break object integrity. Here
is any example:

class SecureRunner
# This class implements a sudo-like
# runner

  def initialize(command)
  # Creates an instance. Guaranties, that a command is safe.
    if command.safe?
      @comamnd = command
    else
      raise RuntimeError, "Security check failed!"
    end
  end

  def run
  # Only safe commands should be run
    system(@command)
  end
end

# This class seems to be safe
# Here is a way to bypass security check:

command = "some_safe_command"
runner = SecureRunner.new(command)
# a command is safe, so check will be passed

command.replace("evil_command") # BYPASS THE CHECK

runner.run # runs evil_command, that is not safe

The same can be done to fields of instances, which are exported as
read-only (attr_reader). I know there is a way to fix it (using .clone
or .dup), but what is the reason Ruby has non-constant strings, as most
languages (Java, Python) do? Is there a way to disable such behaviour
($SAFE will not help, because internal class methods will not be able to
change instance-variable strings too).

···

--
Posted via http://www.ruby-forum.com/.

Hello. Why does Ruby have non-constant strings? It seems there is a way
to bypass object encapsulation paradigm and break object integrity. Here
is any example:

class SecureRunner
# This class implements a sudo-like
# runner

  def initialize(command)
  # Creates an instance. Guaranties, that a command is safe.
    if command.safe?

This will result in a no method error, no?

      @comamnd = command
    else
      raise RuntimeError, "Security check failed!"
    end
  end

  def run
  # Only safe commands should be run
    system(@command)
  end
end

# This class seems to be safe
# Here is a way to bypass security check:

command = "some_safe_command"
runner = SecureRunner.new(command)
# a command is safe, so check will be passed

command.replace("evil_command") # BYPASS THE CHECK
runner.run # runs evil_command, that is not safe

Well, this is not the fault of the language, rather your SecureRunner
class.

def run
     # Only safe commands should be run
     if @command.tainted?
        raise RuntimeError, "Security check failed!"
     end
     system(@command)
end

The same can be done to fields of instances, which are exported as
read-only (attr_reader).

This is the case any language where arguments are passed by reference.

but what is the reason Ruby has non-constant strings

You mean mutable strings. Ruby does have constant strings:

irb(main):001:0> CONST="assbasscass"
=> "assbasscass"
irb(main):002:0> CONST=123
(irb):2: warning: already initialized constant CONST
=> 123

The String class wraps an array of chars, realloc()'in as necessary
(ruby hackers correct me if I've mistakin).

Consider this in Java:

class SomeClassThatRequiredAlotOfTyping
{
  private StringBuffer sb
  //....
  public void printBuffer() { System.out.println(sb); }
}

StringBuffer sb = new StringBuffer("Dont Chnage!");

SomeClassThatRequiredAlotOfTyping clazz = new
SomeClassThatRequiredAlotOfTyping(sb);
// I'll show that private "read-only" var who's in charge!
sb.delete(0,sb.length());
sb.append("VB6, its ByVal keyword rocked... Not!");
clazz.printBuffer();

Hope that helps.

···

On Aug 7, 2:41 pm, Dmitry Bilunov <km...@kmeaw.com> wrote:

Hello. Why does Ruby have non-constant strings? It seems there is a way
to bypass object encapsulation paradigm and break object integrity.

Ruby lets you shoot yourself in the foot.

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/79333

Is there a way to disable such behaviour ($SAFE will not help, because internal class methods will not be able to change instance-variable strings too).

Nope.

You can search the mailing list archives for further discussion on the mutability of strings:

http://blade.nagaokaut.ac.jp/ruby/ruby-talk/index.shtml

···

On Aug 7, 2007, at 14:41, Dmitry Bilunov wrote:

--
Poor workers blame their tools. Good workers build better tools. The
best workers get their tools to do the work for them. -- Syndicate Wars

as a nuby, i can only vouch for the fundamentals. so pardon me if i say something wrong here :slight_smile:

# command = "some_safe_command"

  here command var is pointing to string object "some_safe_command"

# runner = SecureRunner.new(command)
                        ^^^^^^^^^^^^^
               here command ptr is passed
           
# command.replace("evil_command") # BYPASS THE CHECK

   ah, you used replace. This will replace the contents to wch command var is pointing (to wch in turn you passed to securerunner). you can use instead

   command = "evil command"

   command var will now hold a new and different ptr to object string "evil command" (and lose the ptr to "safe_command" wc you passed to saferunner). it's your code :slight_smile:

# runner.run # runs evil_command, that is not safe

  there is a problem with your runner.
  your instance must have no knowledge of the outside.

eg, this is just one stupid example,

C:\temp>cat test.rb
class SecureRunner
  def initialize(command)
      @command = command.dup #<-- i duped. now @command is diff object
  end
  def run
    #system(@command)
    puts "i just ran #{@command}"
  end
end
command = "some_safe_command"
runner = SecureRunner.new(command)
command.replace("evil_command")
runner.run

C:\temp>ruby test.rb
i just ran some_safe_command
C:\temp>

now if you really do not want to change string objects. You can freeze them. But be careful, there is no mr unfreeze yet :slight_smile:

kind regards -botp

···

From: Dmitry Bilunov [mailto:kmeaw@kmeaw.com] :

Hi,

···

2007/8/7, Dmitry Bilunov <kmeaw@kmeaw.com>:

Hello. Why does Ruby have non-constant strings? It seems there is a way
to bypass object encapsulation paradigm and break object integrity. Here
is any example:

class SecureRunner
# This class implements a sudo-like
# runner

  def initialize(command)
  # Creates an instance. Guaranties, that a command is safe.
    if command.safe?
      @comamnd = command
    else
      raise RuntimeError, "Security check failed!"
    end
  end

  def run
  # Only safe commands should be run
    system(@command)
  end
end

# This class seems to be safe
# Here is a way to bypass security check:

command = "some_safe_command"
runner = SecureRunner.new(command)
# a command is safe, so check will be passed

command.replace("evil_command") # BYPASS THE CHECK

runner.run # runs evil_command, that is not safe

The same can be done to fields of instances, which are exported as
read-only (attr_reader). I know there is a way to fix it (using .clone
or .dup), but what is the reason Ruby has non-constant strings, as most
languages (Java, Python) do? Is there a way to disable such behaviour
($SAFE will not help, because internal class methods will not be able to
change instance-variable strings too).
--
Posted via http://www.ruby-forum.com/\.

You want to freeze the object @command, not the class String, don't
you ?
So you don't need to use an Immutable_String class, just add
@command.freeze in your code

--
Etienne Vallette d'Osia

That is easily fixed:

# Creates an instance. Guaranties, that a command is safe.
def initialize(command)
  # side effect free fix:
  command = command.dup
  # alternative fix: command.freeze
   if command.safe?
     @comamnd = command
   else
     raise RuntimeError, "Security check failed!"
   end
end

Now you can change the original string in as many ways as you like
without doing any harm. As easy as that.

And I'd like to add it's not the fault of the language if code like
this fails. In fact there are numerous arguments in favor of having
mutable *and* immutable strings vs. having mutable strings only.

Kind regards

robert

···

2007/8/7, Dmitry Bilunov <kmeaw@kmeaw.com>:

Hello. Why does Ruby have non-constant strings? It seems there is a way
to bypass object encapsulation paradigm and break object integrity. Here
is any example:

class SecureRunner
# This class implements a sudo-like
# runner

  def initialize(command)
  # Creates an instance. Guaranties, that a command is safe.
    if command.safe?
      @comamnd = command
    else
      raise RuntimeError, "Security check failed!"
    end
  end

  def run
  # Only safe commands should be run
    system(@command)
  end
end

# This class seems to be safe
# Here is a way to bypass security check:

command = "some_safe_command"
runner = SecureRunner.new(command)
# a command is safe, so check will be passed

command.replace("evil_command") # BYPASS THE CHECK

runner.run # runs evil_command, that is not safe

Skye Shaw!@#$ wrote:

    if command.safe?

This will result in a no method error, no?

It is just an example, assume that you have some safety-checking code
here.

Well, this is not the fault of the language, rather your SecureRunner
class.

Yes, it is not fault of Ruby, I am just trying to understand, why do the
strings work in a such way. Most operations can be done with immutable
strings - an operation can return a new string, constructed from method
caller and, optionally, arguments.

This is the case any language where arguments are passed by reference.

but what is the reason Ruby has non-constant strings

You mean mutable strings. Ruby does have constant strings:

irb(main):001:0> CONST="assbasscass"
=> "assbasscass"
irb(main):002:0> CONST=123
(irb):2: warning: already initialized constant CONST
=> 123

The String class wraps an array of chars, realloc()'in as necessary
(ruby hackers correct me if I've mistakin).

Ruby string has array of chars, length (logical array size) and capacity
(physical array size, probably larger, that logical) as Java's
StringBuffer does.

Consider this in Java:

class SomeClassThatRequiredAlotOfTyping
{
  private StringBuffer sb
  //....
  public void printBuffer() { System.out.println(sb); }
}

StringBuffer sb = new StringBuffer("Dont Chnage!");

SomeClassThatRequiredAlotOfTyping clazz = new
SomeClassThatRequiredAlotOfTyping(sb);
// I'll show that private "read-only" var who's in charge!
sb.delete(0,sb.length());
sb.append("VB6, its ByVal keyword rocked... Not!");
clazz.printBuffer();

Hope that helps.

But Java has also String, not just StringBuffer. Is there anything like
String to protect a program against such things? Or some version of
.freeze, which freezes a variable (field) to anyone, except instance
methods?

···

On Aug 7, 2:41 pm, Dmitry Bilunov <km...@kmeaw.com> wrote:

--
Posted via http://www.ruby-forum.com/\.

Eric Hodel wrote:

···

On Aug 7, 2007, at 14:41, Dmitry Bilunov wrote:

Hello. Why does Ruby have non-constant strings? It seems there is a
way
to bypass object encapsulation paradigm and break object integrity.

Ruby lets you shoot yourself in the foot.

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/79333

Is there a way to disable such behaviour ($SAFE will not help,
because internal class methods will not be able to change instance-
variable strings too).

Nope.

You can search the mailing list archives for further discussion on
the mutability of strings:

http://blade.nagaokaut.ac.jp/ruby/ruby-talk/index.shtml

Thanks. Mailing list discussion has exactly what I was looking for - the
explanation of the reason Ruby has mutable strings.
--
Posted via http://www.ruby-forum.com/\.

dohzya wrote:

Hi,

  # Creates an instance. Guaranties, that a command is safe.
  end

You want to freeze the object @command, not the class String, don't
you ?
So you don't need to use an Immutable_String class, just add
@command.freeze in your code

It will make impossible changing @command inside instance methods (which
can be guaranteed to be safe). If there is a way to unfreeze a frozen
object, then this method could be called externally and one can gain
write access to an internal instance variable.

···

2007/8/7, Dmitry Bilunov <kmeaw@kmeaw.com>:

--
Posted via http://www.ruby-forum.com/\.

Not exactly.

@command is just a pointer to a string, incidentally pointing to same
string as command pointer points to.

So you have three things: 1. the string "safe_command" 2. variable
(=pointer) command, 3. member variable (=pointer) @command.

Now, if you issue @command.freeze, you are not freezing the pointer
@command, but the string it points to, i.e. "safe_command". So if you
do @command.freeze, it is the same as doing "safe_pointer".freeze.

After @command.freeze, you cannot do command.replace - because command
points to a frozen string. However, you can still do command = "abc",
i.e. make command point to another string.

Finally after @command.freeze, you can't do @command.replace either.
But you can do @command = "adsfg" and thus make the pointer point to
another string.

In fact, Hash#= dups and freezes String keys, just for the same reason.

Jano

···

On 8/8/07, Dmitry Bilunov <kmeaw@kmeaw.com> wrote:

dohzya wrote:
> Hi,
>
> 2007/8/7, Dmitry Bilunov <kmeaw@kmeaw.com>:
>> # Creates an instance. Guaranties, that a command is safe.
>> end
>>
>>
>>
> You want to freeze the object @command, not the class String, don't
> you ?
> So you don't need to use an Immutable_String class, just add
> @command.freeze in your code

It will make impossible changing @command inside instance methods (which
can be guaranteed to be safe). If there is a way to unfreeze a frozen
object, then this method could be called externally and one can gain
write access to an internal instance variable.

Jano Svitok wrote:

It will make impossible changing @command inside instance methods (which
can be guaranteed to be safe). If there is a way to unfreeze a frozen
object, then this method could be called externally and one can gain
write access to an internal instance variable.

Not exactly.

[cut]

In fact, Hash#= dups and freezes String keys, just for the same
reason.

Jano

Thanks, now it makes sense for me.

···

On 8/8/07, Dmitry Bilunov <kmeaw@kmeaw.com> wrote:

--
Posted via http://www.ruby-forum.com/\.

Shortest, closest thing I can think of for unfreezing an object is to
dup it.

a = "test" --> "test"
a.freeze --> 1
a << "test" --> TypeError
a = a.dup --> 1
a << "test" --> "testtest"

(sorry about the non-irb format...default command shell in WinXP
doesn't allow copy/paste. *sigh*)

···

On Aug 8, 7:51 am, "Jano Svitok" <jan.svi...@gmail.com> wrote:

On 8/8/07, Dmitry Bilunov <km...@kmeaw.com> wrote:
> If there is a way to unfreeze a frozen
> object, then this method could be called externally and one can gain
> write access to an internal instance variable.

Not exactly.

> > If there is a way to unfreeze a frozen
> > object, then this method could be called externally and one can gain
> > write access to an internal instance variable.
>
> Not exactly.

Shortest, closest thing I can think of for unfreezing an object is to
dup it.

a = "test" --> "test"
a.freeze --> 1
a << "test" --> TypeError
a = a.dup --> 1
a << "test" --> "testtest"

Just to make it clear: this is not unfreezing - it will just create a
new instance that is not frozen so the original is still safe.

(sorry about the non-irb format...default command shell in WinXP
doesn't allow copy/paste. *sigh*)

You can copy and past from any Windows command line shell. You can
even configure it so you can directly mark with the mouse and copy by
pressing enter.

Kind regards

robert

···

2007/8/8, Kaldrenon <kaldrenon@gmail.com>:

On Aug 8, 7:51 am, "Jano Svitok" <jan.svi...@gmail.com> wrote:
> On 8/8/07, Dmitry Bilunov <km...@kmeaw.com> wrote:

Just to make it clear: this is not unfreezing - it will just create a
new instance that is not frozen so the original is still safe.

see by your eyes :

s = "test"

=> "test"

a = s

=> "test"

a.freeze

=> "test"

a << "test"

TypeError: can't modify frozen string
        from (irb):4:in `<<'
        from (irb):4

a = a.dup

=> "test"

a << "test"

=> "testtest"

s

=> "test"

s is frozen and not changed

···

Le mercredi 08 août 2007 à 23:04 +0900, Robert Klemme a écrit :

2007/8/8, Kaldrenon <kaldrenon@gmail.com>:

--
Etienne Vallette d'Osia

I am not sure what you are trying to insinuate. Your code just
stresses my point: there is a difference between the imaginary
"a.unfreeze" and "a = a.dup" because in the first example there is
just one instance involved while in the second there are two. Any
piece of code that holds on to the original will still have a
reference to the original with the dup approach. Both approaches are
*not* equivalent, which might seem like a subtlety to some - but it is
a crucial point to remember.

Kind regards

robert

···

2007/8/8, dohzya <dohzya@gmail.com>:

Le mercredi 08 août 2007 à 23:04 +0900, Robert Klemme a écrit :
> 2007/8/8, Kaldrenon <kaldrenon@gmail.com>:
> Just to make it clear: this is not unfreezing - it will just create a
> new instance that is not frozen so the original is still safe.

see by your eyes :