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).

On Aug 7, 2:41 pm, Dmitry B. [email protected] wrote:

if command.safe?

This will result in a no method error, no?

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.

Skye Shaw!@#$ wrote:

On Aug 7, 2:41 pm, Dmitry B. [email protected] 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, 2007, at 14:41, Dmitry B. 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

Eric H. wrote:

On Aug 7, 2007, at 14:41, Dmitry B. 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.

Hi,

2007/8/7, Dmitry B. [email protected]:

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

From: Dmitry B. [mailto:[email protected]] :

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

On 8/8/07, Dmitry B. [email protected] wrote:

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.

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

Jano S. wrote:

On 8/8/07, Dmitry B. [email protected] 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.

2007/8/7, Dmitry B. [email protected]:

if command.safe?

end
runner.run # runs evil_command, that is not safe
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

On Aug 8, 7:51 am, “Jano S.” [email protected] wrote:

On 8/8/07, Dmitry B. [email protected] 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.

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)

dohzya wrote:

Hi,

2007/8/7, Dmitry B. [email protected]:

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.

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

2007/8/8, Kaldrenon [email protected]:
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

2007/8/8, dohzya [email protected]:

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

2007/8/8, Kaldrenon [email protected]:
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 :

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, Kaldrenon [email protected]:

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