LWN.net Logo

Virtual Machines and Memory Protections

November 20, 2006

This article was contributed by John Richard Moser

The IT industry and the open source community both currently enjoy a healthy want for security, a growing passion that has brought about new security tools and even some new programming languages. It isn't always easy to get all of these things working together; virtual machines such as Mono, for example, have difficulty with the memory space policies enforced by PaX or SELinux. Some implementations of the CLI virtual machine may have difficulty functioning with these security protections, and may be exposed to native code called from C# programs or the virtual machine itself.

The C# programming language is gaining popularity, and has been used to write programs such as Beagle, F-Spot, and Banshee. It is also a supported language for development in the GNOME environment. C# has strong type checking, array bounds checking, detection of attempts to use uninitialized variables, and automatic garbage collection, making it both type-safe and memory-safe; these aspects make it an attractive language for developers who want to sidestep manual memory management and just get their programs working.

C# programs are typically compiled to Common Instruction Language (CIL), a bytecode language designed to be run inside a virtual machine implementing the Common Language Infrastructure (CLI). Bytecode languages are similar to machine-level instructions, except they're not hosted on a physical CPU; effectively they are CPU architectures that are only run on emulators. Another familiar example of this is the Java platform, the typical target of the Java programming language.

The most naive approach to bytecode execution is to use an interpreter. Interpreters read each instruction in the program as executed; determine what the instruction is; and then modify the state of the virtual machine as needed, changing memory values or the program execution point. Interpreters execute dozens of instructions each time they process a bytecode instruction; programs execute very slowly, with all but the simplest being irritatingly sluggish.

Virtual machines often use a technique called Just-in-Time compilation (JIT) to improve performance. Rather than interpret, JIT compilers generate equivalent native code from the bytecode they encounter; in essence, they translate the parts of the program being run to run natively as encountered. Because of this, the continuous interpreter cost becomes a series of short one-time compilation costs, which in most cases goes unnoticed.

The first time I wrote for LWN, I authored a small article on security improving technologies which could be deployed now. Since then, these and other technologies have become more prevalent; ProPolice is part of gcc, and some of the concepts behind PaX and grsecurity are now integrated into products such as Exec Shield and SELinux. SELinux has policy elements that can be applied to almost exactly mimic the behavior of mprotect() under PaX.

Briefly put, both PaX and SELinux supply a set of protections that prevent programs from executing any memory that could have ever been directly altered by the program itself. A typical exploit technique is to use a flaw in a program to cause it to execute an area of memory an attacker loaded with code; with these restrictions, this attack is no longer possible. The attackers are forced then to resort to executing existing code out of order, which is a blind shot at a moving target due to address space randomization.

These protections are highly significant; however, they interfere in an unfortunate way with the execution of programs on Just-in-Time (JIT) mechanisms such as those used in Mono. The JIT needs to write code into memory and execute it; and the security system won't allow code generated at runtime to run. Since the interpreter is far too slow to be useful, the only real option is to disable the security mechanisms that interfere with the JIT.

The Common Language Infrastructure (CLI) allows for managed code to access unmanaged code; in other words, C# code can call plain old C libraries, making the program as a whole vulnerable to flaws that can't exist in C#. The implementation of the virtual machine is also a factor: Mono implements Web browser features using Mozilla's Gecko rendering engine; and Java implementations can, for example, use libpng bindings to supply PNG image handling rather than full managed rewrites.

Below are listed a couple popular Mono applications—C# and other CLI applications that run on Mono—using native libraries; as well as some of those libraries that have had significant security holes allowing remote runtime code execution.

With this potential for vulnerability, it would be attractive to find a solution for executing Mono without using the JIT. To execute CLI applications without a JIT, Mono would have to provide a method of executing assemblies without rewriting them into native code at runtime. This method would have to function both for typical CIL code and for dynamic assembly. Dynamic assembly is used to generate CIL bytecode at runtime, which is then executed by Mono with the help of the JIT. The Cecil debugger; IronPython; and the IKVM Java runtime are examples of programs that use dynamic assembly to execute whole programs.

The most naive method would be to switch back to the interpreter. Unfortunately we've already established that the interpreter is extremely slow, requiring dozens of cycles to complete even the simplest addition or variable assignment. Even if the interpreter didn't have such prohibitive performance issues, it's not really supported anywhere the JIT works, and isn't actively maintained.

Another possibility is to use the Ahead-of-Time (AOT) compiler to run Mono programs. The AOT compiles Mono assemblies to native code and stores them as shared libraries. AOT modules can be cached, verified, and updated as needed. This allows Mono to dlopen() the generated code and execute it like any other library. This not only eliminates runtime code generation; but also also increases code sharing between applications, reducing overall system memory usage. Unfortunately, dynamic assembly doesn't work with AOT, because it cannot be cached and verified later.

Ulrich Drepper described method of double-mapping a file, in which the same memory is available in two different places under two different permission sets. The file is created, opened, and unlinked so no other program can alter it; and then mmap() is used to make two shared mappings, one writable and one executable. This would work; but it would also increase disk access and use more of the task's virtual address space. It would also still allow a very obscure, unlikely, but possible method for directly introducing code into a program's address space and executing it successfully.

Currently there doesn't seem to be an obvious great solution to get Mono to run without runtime code generation. The interpreter is too slow; AOT doesn't cover dynamic assembly; and Drepper's method of double-mapping a file creates more disk access. Hybrid methods such as AOT with double-mapping for dynamic assemblies are also possible, reducing the severity of some of the drawbacks. By combining these methods, varying degrees of immunity to remote code execution are afforded with corresponding cost trade-offs.

Of interesting note is that double-mapping a file would prevent policy from being used to restrict the program to mapping only system libraries and a global AOT cache. Apart from the unlikely special case with double-mapping, enhanced memory protections will guarantee that an attacker cannot directly introduce code into a running program; however, attacks that use return-to-libc chains can still create, mmap(), and execute a file. To prevent this, one could restrict executable file-backed mappings to directories only the system administrator can write to, such as system libraries and a global AOT cache; of course, this would break double-mapping.

I cannot predict the implications of these facts for trusted systems and the applications of C# and Mono in high-security environments. For my own purposes, I would prohibit the use of Mono programs in environments with strong security requirements. In my perspective, the cost and potential for error involved in manually auditing all native code in both the Mono virtual machine and any native code used by Mono applications simply does not supply enough value; it is much easier to utilize protections against classes of vulnerabilities than to prove that applications do not need said protections. Your mileage may vary.


(Log in to post comments)

Virtual Machines and Memory Protections

Posted Nov 22, 2006 3:53 UTC (Wed) by jwb (guest, #15467) [Link]

Or, you could just write your software in Java.

Virtual Machines and Memory Protections

Posted Nov 22, 2006 4:18 UTC (Wed) by bluefoxicy (guest, #25366) [Link]

Back in the day, Linux had no threading. There was a Green Threads library that faked it; Java called that for threading.

What does the Sun JVM use for various parts of Java? How about Kaffe? How about Apache's Project Harmony? GNU Classpath? Do they all use their own complete rewrites of everything in Java?

What is the JVM written in? Does it have bugs? (Sun's has had a few security holes itself, mostly though you would have to pass it broken Java bytecode to trigger these)

Writing something in Java has the same effect as writing it in C#: You don't know about the rest of the program, but the code YOU WROTE is immune to X Y and Z class of bugs. You're not even sure what virtual machine implementation the code is going to run on (Mono/IKVM, Kaffe, Harmony, Sun Java, IBM Java, GNU Classpath, proprietary in-house...).

Java and Memory Protections

Posted Nov 22, 2006 8:49 UTC (Wed) by tzafrir (subscriber, #11501) [Link]

Java has basically the same problem, right?

I'm surprised that the article has not mentioned it.

Java and Memory Protections

Posted Nov 22, 2006 13:23 UTC (Wed) by NAR (subscriber, #1313) [Link]

I'm not sure Java enables to dynamically create code. At least I haven't generated byte code dynamically, but I might have read something about this. Actually I've written self-modifying code in LISP, so I guess there is a problem with that language as well.

Bye,NAR

Java and Memory Protections

Posted Nov 22, 2006 14:16 UTC (Wed) by smitty_one_each (subscriber, #28989) [Link]

Isn't dynamic code generation a natural consequence of a virtual machine coupled with reflection?
If my understanding of what the Apache Tomcat code was correct back in '03, at boot-time, the XML configuration files got parsed and instantiated all of the security classes for the app server via reflection: the XML was a de facto scripting language for the JVM.

Java and Memory Protections

Posted Nov 22, 2006 14:49 UTC (Wed) by NAR (subscriber, #1313) [Link]

Isn't dynamic code generation a natural consequence of a virtual machine coupled with reflection?

I'm not sure - in my mind, reflection is about getting runtime information about the objects, it has nothing to do with creating anything. Anyway, as far as I understand, the problem with memory protection is not simply the dynamic code generation, but the compilation of the generated code to machine-level instructions. So even though one can write self-modifing code in LISP or perl, it's not a problem until the interpreter executes this code, not the CPU directly.

Bye,NAR

Java and Memory Protections

Posted Nov 22, 2006 15:40 UTC (Wed) by gouyou (subscriber, #30290) [Link]

From the article the problematic code generated is the native code executed by the processor. So in fact I believe the problem exist for all language using a VM using a Just-in-Time compiler: C#, Java, but also future version of Python, Perl running on a VM doing JiT compilation.

The security problem won't anyway be fixed by PaX or SELinux. Any language providing an eval function will have this type of problem, just at a higher level.

Java and Memory Protections

Posted Nov 22, 2006 16:18 UTC (Wed) by NAR (subscriber, #1313) [Link]

The security problem won't anyway be fixed by PaX or SELinux. Any language providing an eval function will have this type of problem, just at a higher level.

Yes, consider this bash code:


X="/bin/r"
Y="m -f /"
Z=$X$Y
eval $Z

If the values of X and Y are not hardcoded, but come from the untrusted input, then it's a problem. That's why things like the -T option of perl got invented - I wonder if other languages/VMs have options like this.

Bye,NAR

Java and Memory Protections

Posted Nov 22, 2006 19:13 UTC (Wed) by bluefoxicy (guest, #25366) [Link]

> The security problem won't anyway be fixed by PaX or SELinux.

You're right that higher-level languages misfiring "eval" statements can't be addressed; but this is not what I was referring to. What can be "fixed" by PaX or SELinux is the method of attack possible on broken native code. Consider the below.

===Begin Attack Model===

PROBLEM:
1. Buffer overflow (stack)
2. Buffer overflow (heap)
3. double-free

RESULT:
1. Attacker redirects instruction pointer
2. Attacker may execute foreign code (i.e. local root exploits)

SOLUTION 1:
1. No memory may be created WRITE and EXECUTE; and
2. No memory without EXECUTE may be granted EXECUTE

Attacker cannot directly write in code to execute, then carry out attack (memory-level W^X).

WORKAROUND 1:
1. Return to open(); then
2. return to write(); then
3. Return to mmap(), mmap() file executable; then
4. Return to mapped file (rootkit code)

SOLUTION 2:
1. SOLUTION 1; and
2. Employ mandatory access restrictions on what files can be mmap()'d executable. Limit to files in directories not writable by unprivileged users.

Attacker cannot use WORKAROUND 1, because he can't write a file and then mmap() it executable (filesystem-level W^X).

WORKAROUND 2:
Create a complex return chain through various bits of code of the
following structure:

@entry:
interesting_code
... (code that doesn't mess up our attack)
RET

This is highly unlikely to be possible or easy in most cases; often
this will be out of the attacker's scope. Attackers may keep a list
of interesting opcode locations to help speed up the creation of
these types of payloads, however.

See http://uninformed.org/?v=2&a=4&t=sumry for an attack that actually uses this bypass method.

===End Attack Model===

The above will allow most programs to execute properly; but will interfere with most memory-corruption-based security attacks. In the case of JIT, these protections prevent the JIT from ever working.

Usually, JIT is used for languages that you can't experience these kinds of bugs in. The article explains that the underlying C code and the other native code being linked in may still have these bugs, and can't fall under the protection umbrellas normally possible.

"Safe" languages are interesting and hopefully at least a partial solution can be found. A global AOT cache might work exclusively for code that doesn't use reflection...

Java and Memory Protections

Posted Nov 22, 2006 18:11 UTC (Wed) by Per_Bothner (subscriber, #7375) [Link]

Java does support dynamically generated classes. This is used by Kawa for its evaluator: Kawa reads a high-level program (Scheme, XQuery, Emacs Lisp), translates it to Java bytecodes, dynamically creates a class, and then calls a method in the class. It is quite fast, allowing for a very responsive read-eval-print-loop combined with fast execution.

Java and Memory Protections

Posted Nov 22, 2006 18:19 UTC (Wed) by iabervon (subscriber, #722) [Link]

Dynamic generation of Java bytecode is actually quite common; that's how applets actually work. The plugin, running in a JVM, downloads code from the internet as a sequence of bytes, and calls the library method to read the memory as classes. Sure, it's not generation of bytecode from scratch, but it is pulled out of nowhere with respect to memory protection and certainly ahead-of-time compilation.

Java and Memory Protections

Posted Nov 24, 2006 18:45 UTC (Fri) by ttfkam (guest, #29791) [Link]

This is incorrect. This is not how applets work. Applets use classloaders (network classloaders in this case) exactly like any standalone Java application might.

JSPs have sort-of done this for years by compiling whenever the source doc is modified. Another example would be the "translets" created from XSLT stylesheets tranformations by the XSLTC.

A better example of dynamic code generation would be frameworks that implement EJB3 and read information from annotations to generate code that fits those aspect-oriented patterns.

But that's higher level. From the implementation point of view, dynamic code generation is implemented from libraries like ASM, JavaCC, cglib, etc.

Java and Memory Protections

Posted Dec 3, 2006 12:54 UTC (Sun) by fjalvingh (guest, #4803) [Link]

You are both right. A ClassLoader is used to create the class. But the classloader used for Applets (which is a a standard one) indeed merely loads a stream of bytes and instantiates these as a Class using a ClassLoader base method [ Class defineClass(String name, byte[] b, int off, int len)].

In Java it is trivial to write and execute your own byte code from within a program; whole libraries exist to help you doing it (BCEL, ASM). I used this extensively in many programs to speed up scripting stuff or parameterized logic by compiling it into a class (which will then be JITted by the Java runtime).

Anyway, the fact that Java code can do that has nothing to do with the article's subject which has to do with actual executable content for a system processor...

Java and Memory Protections

Posted Nov 22, 2006 18:20 UTC (Wed) by jwb (guest, #15467) [Link]

To me, the meat of the problem is that "safe" code (C#, Java) is allowed to call native libraries. It then becomes possible for the "safe" code to exploit flaws in those native libraries, which is obviously unsafe.

The nice thing about Java is you can easily forbid calling native code.

Java and Memory Protections

Posted Nov 22, 2006 19:16 UTC (Wed) by bluefoxicy (guest, #25366) [Link]

> The nice thing about Java is you can easily forbid calling native code.

C# can do this as well. The thing is, sometimes you actually need the functionality present in the native libraries. F-Spot uses libpng to show PNG images, and libjpeg to show JPEGs. As well, Mono uses Gecko and .NET uses IE to supply the Internet Explorer Web control from .NET, which has obvious implications; if you want to write a Web browser, are you going to be forced to write it all in C#, including render engine?

Java and Memory Protections

Posted Nov 22, 2006 19:22 UTC (Wed) by jwb (guest, #15467) [Link]

I don't see why not. Java provides pure Java (not native) PNG and JPEG routines, as well as pure Java cryptography, pure Java compression, and the rest. Oh, and there are also pure Java web browsers.

Java and Memory Protections

Posted Nov 22, 2006 19:25 UTC (Wed) by bluefoxicy (guest, #25366) [Link]

Sun Java does. What about GNU Classpath? Kaffe? Project Harmony? IKVM?

Java and Memory Protections

Posted Dec 3, 2006 12:57 UTC (Sun) by fjalvingh (guest, #4803) [Link]

One good reason to go native is performance.. Although Java performance is usually great you can get huge performance gains using assembly for specialized problems (like image encoding/decoding/filtering for instance)

Virtual Machines and Memory Protections

Posted Nov 22, 2006 6:09 UTC (Wed) by skissane (subscriber, #38675) [Link]

Related to Ulrich Drepper's proposal to double mmap a file, here is my idea: Have a new system call (maybe double_mmap). It is passed in a number of pages, which it then allocates. It then maps those pages twice into your address space, at two different addresses: at one place as read-write, at the other as read-execute. No disk space needed; virtual address space is doubled, but physical memory usage stays the same. The kernel could ensure that both virtual addresses are random. It could also ensure that no other process can get access to those address spaces. Doing this would still make JIT possible, but should give attackers a lot harder time, since they would have to guess not only where the memory to write to is, then also guess where the corresponding execute location is.

Virtual Machines and Memory Protections

Posted Nov 22, 2006 11:45 UTC (Wed) by nix (subscriber, #2304) [Link]

Something like mmalias() seems more useful: hand it a memory area and it creates an alias to it in a new VMA (and thus at a new virtual address) and hands it back to you. Then you can mprotect() it howsoever you wish.

Of course subverted code can now call this as well, but that's in general true of *any* means of allowing dynamically-generated code. If your code can do it as part of its normal operation, there's no way to statically tell if it's been subverted into doing it.

Virtual Machines and Memory Protections

Posted Nov 22, 2006 11:45 UTC (Wed) by nix (subscriber, #2304) [Link]

(mmalias() doesn't exist, I just made it up. Still it seems possible.)

Virtual Machines and Memory Protections

Posted Nov 22, 2006 19:18 UTC (Wed) by bluefoxicy (guest, #25366) [Link]

> Of course subverted code can now call this as well, but that's in general true of *any* means of allowing dynamically-generated code. If your code can do it as part of its normal operation, there's no way to statically tell if it's been subverted into doing it.

Exactly the point. Comment posted at http://lwn.net/Articles/210804/ goes into great detail about protecting against these attacks; but it all boils down to what you just said: If you can dynamically generate code, so can an attacker hijacking you.

Virtual Machines and Memory Protections

Posted Nov 22, 2006 11:05 UTC (Wed) by jospoortvliet (subscriber, #33164) [Link]

so if you use any non-java library in java, or a non-mono library in mono,
you're screwed, right?
and linux is all about code-reuse, mostly in the form of libraries, right?

what about ruby, python, perl?

so security isn't a C# or java advantage, on the contrary. nor is
performance. what is, then?
it's fast to write apps in? is it seriously faster to write lets say a
music player a la amarok or a app like gaim/kopete in java/C# compared to
C/GTK2+/Gnome? and how does it compare to C++/Qt/KDE?

Virtual Machines and Memory Protections

Posted Nov 22, 2006 14:59 UTC (Wed) by NAR (subscriber, #1313) [Link]

so security isn't a C# or java advantage, on the contrary.

Not exactly. In case of C# or Java, these kind of memory-corruption bugs can be only in the virtual machine, in the JIT compiler and in the code generated by the JIT compiler, not in the actual code written by the developers. It's a lot less code to review and make it bugfree, compared to C where any code can overflow a buffer, not just the code in gcc.

Bye,NAR

Virtual Machines and Memory Protections

Posted Nov 22, 2006 15:43 UTC (Wed) by gouyou (subscriber, #30290) [Link]

Plus java has a feature where you can restrict the VM to only run signed code.

Virtual Machines and Memory Protections

Posted Nov 22, 2006 18:59 UTC (Wed) by bluefoxicy (guest, #25366) [Link]

> In case of C# or Java, these kind of memory-corruption bugs can be only in the virtual machine, in the JIT compiler and in the code generated by the JIT compiler, not in the actual code written by the developers.

Almost. If by "virtual machine" you also meant "and all the libraries it uses, such as Gecko or libpng," you're very close. Remember C# can also call non-C# code, as noted in the article.

Virtual Machines and Memory Protections

Posted Nov 22, 2006 19:24 UTC (Wed) by bluefoxicy (guest, #25366) [Link]

> so security isn't a C# or java advantage, on the contrary. nor is performance. what is, then?

The language itself is type-safe and memory safe, which is a huge advantage. See the big comment for an explanation of threats and mitigations for type-unsafe languages. A third solution is to use a new language that doesn't suffer those particular flaws, which is what C# and Java are.

It may be possible to run some C# programs exclusively using AOT compilation. Programs that don't use reflection (I suspect most) would be perfectly safe to pre-compile and load with the AOT mechanism. Things like IKVM or IronPython .. well, they need JIT or they become excessively slow.

Virtual Machines and Memory Protections

Posted Nov 22, 2006 11:42 UTC (Wed) by nix (subscriber, #2304) [Link]

double-mapping a file doesn't create more disk access if you create it on a tmpfs filesystem. user-mode-linux already has to do exactly that to map the guest's 'physical' RAM, so this is not a new trick, and really does work.

Virtual Machines and Memory Protections

Posted Nov 22, 2006 13:21 UTC (Wed) by davecb (subscriber, #1574) [Link]

You need a (fine-grained) permission/capability, such as implemented
in AppArmour or SELinux, to set a page to write and execute. You then grant that permission to only the one or two JIT compilers that you need.

--dave

Virtual Machines and Memory Protections

Posted Nov 22, 2006 19:28 UTC (Wed) by bluefoxicy (guest, #25366) [Link]

Exactly what the article is about. Once the JIT compiler is allowed to do such things, it's a free-for-all. They call mmap() or mprotect(), which goes into glibc, which means the kernel doesn't know who's doing what. If you happen to have exploitable code from some native library that makes up the JIT or that's called from C# code, you're allowing that native library the same permissions.

Virtual Machines and Memory Protections

Posted Nov 22, 2006 22:59 UTC (Wed) by davecb (subscriber, #1574) [Link]

Hmmn, I think you're assuming something I'm not...

The JIT compiler has the permission to set a page "wx", which it
employs to set the pages it writes code into as executable. When
it's actually calling mmap or mprotect, the process context is
present, and the kernel can see if it actually has the permission
before it makes the change, so arbitrary callers of mmap, mprotect
and libc can't do privilege escalation attacks.

To prevent a called library or JIT'd code from doing privilege escalation,
the compiler needs to shed any special permissions before passing
control to the compiled code. This is normal practice, rather
like doing a setuid back to the ordinary user when you're not doing
anything priviliged in security-aware programs like Samba or NFS.

Does that address the points you were referring to, or did you mean something different?

--dave

Virtual Machines and Memory Protections

Posted Nov 23, 2006 19:20 UTC (Thu) by bluefoxicy (guest, #25366) [Link]

> When it's actually calling mmap or mprotect, the process context is present, and the kernel can see if it actually has the permission before it makes the change

Consider that the kernel doesn't know who called mmap() or mprotect() though. mmap() goes into mmap@@GLIBC, which goes into syscall@@GLIBC, which enters the kernel at the syscall entry point plus the offset for mmap(). Same with mprotect().

The kernel isn't tracking code call paths (this is difficult, would be very slow, and can be spoofed); it's the entire process that's allowed to do it. If you say the JIT is allowed to call mprotect(PROT_WRITE|PROT_EXECUTE), and the JIT links with libxml2, then a bug in libxml2 will allow an attacker to mprotect(PROT_WRITE|PROT_EXECUTE) ONLY in the JIT process and not in any other program using libxml2.

I'm trying to think of a way to contextually control calls, but right now there's no mechanism and nothing I can prove secure. I will think about it.

Virtual Machines and Memory Protections

Posted Nov 24, 2006 1:16 UTC (Fri) by davecb (subscriber, #1574) [Link]

Goodness gracious! That would make it extremely difficult to
do any decison dependant on who's making the system call, which
depends on having a process context.

Are you sure that's the case in Linux? Unix from v6 onwards
certainly knows
who's calling and what permissions they have. The whole
trusted computing base work (from which fine-grained permissions
come) is completely dependant on that, and SE Linux certainly
has the user's permissions, category and security level...

I can't imagine how normal Linux might avoid that.

--dave

Virtual Machines and Memory Protections

Posted Nov 24, 2006 2:05 UTC (Fri) by bluefoxicy (guest, #25366) [Link]

You have a PROCESS context; however, a process.. let's say /bin/cat.. has libraries it uses. Any code in those libraries is running in that process' context. So if mysql uses libc6, then libc6 code is run with mysql's privileges; while if eye of gnome uses libc6, libc6 code is being executed with the user's privileges.

Look at Exec Shield even. Libraries like libgcrypt11 are marked needing an executable stack; this causes half of GNOME as well as Gaim and Firefox to have an executable stack because they use libgcrypt11. Once this was fixed in Ubuntu, a large number of programs suddenly didn't have an executable stack anymore. It's not like the stack is only executable when libgcrypt11 tries to execute it; if libpng tries to execute code on the stack and normally fails, it'll succeed just because libgcrypt11 is linked to the application.

Short version: Unix/POSIX/Linux can only distinguish between PROCESSES, not CODE. (limitation of the CPU in many, many cases)

fine-grained permissions

Posted Nov 28, 2006 2:22 UTC (Tue) by xoddam (subscriber, #2322) [Link]

I think you've missed the point about fine-grained permissions.
Presumably the JIT compiler only needs to set write+execute on a VM area
once, at startup. Once it has done that, the entire process can drop the
privilege that allows it to do so.

This does not mean that it's impossible for an exploit of a system
library to write and call into that wx VM area once it exists, but
risk-mitigation techniques like address randomisation are just as valid
for JIT object code as they are for mapped .so files.

The point stands that any system that includes 'eval' is vulnerable to
this class of attack, no matter how it is implemented.

Virtual Machines and Memory Protections

Posted Nov 22, 2006 16:26 UTC (Wed) by cventers (guest, #31465) [Link]

Why not double-mmap a region created with shm_open() and ftruncate()?

Virtual Machines and Memory Protections

Posted Nov 22, 2006 19:29 UTC (Wed) by bluefoxicy (guest, #25366) [Link]

Because shared memory objects are never allowed to be executable, even on vanilla Linux. I don't think POSIX allows executable shared memory due to the massive security implications.

Virtual Machines and Memory Protections

Posted Nov 22, 2006 21:59 UTC (Wed) by cventers (guest, #31465) [Link]

Huh. Well, I was taking a guess, but I figured that the permissions you
supplied to shm_open() and mmap() would determine that.

Virtual Machines and Memory Protections

Posted Nov 23, 2006 19:27 UTC (Thu) by bluefoxicy (guest, #25366) [Link]

Read the man page for shmget.

> mode_flags (least significant 9 bits) specifying the permissions granted to the owner, group, and world. These bits have the same format, and the same meaning, as the mode argument of open(2). Presently, the execute permissions are not used by the system.

I may be wrong about POSIX, but Linux disallows shared memory to be executable. Any attempt to use shared memory violating its permissions (i.e. mmap()ing it writable when the caller has only read-only perms) fails.

Yes, shared memory is complex and confusing as crap. Don't feel bad if you don't get it.

Virtual Machines and Memory Protections

Posted Nov 23, 2006 21:42 UTC (Thu) by cventers (guest, #31465) [Link]

> Yes, shared memory is complex and confusing as crap. Don't feel bad if
> you don't get it.

Well, I think SysV shared memory is crap. POSIX shared memory is much
better. shm_open() says nothing about execute permissions in the manual
page. I haven't checked the kernel's implementation, but I thought that
might indicate that it does nothing special there.

vulnerability inflation

Posted Nov 22, 2006 23:49 UTC (Wed) by roelofs (guest, #2599) [Link]

Below are listed ... some of those libraries that have had significant security holes allowing remote runtime code execution.

  • Banshee, a music player that handles a variety of formats.
  • ...

I have to call bullshit on that one. Like far too many of those publishing these advisories, MITRE and/or the "CVE Editorial Board" are far too eager to artificially inflate the severity levels for press purposes, to all appearances--and as far as I can determine, they never correct their mistakes or even properly qualify the threat-level with specific information provided by the original reporter. (I have personal experience with that--though not via libpng, for which I'm incorrectly credited as the "vendor.")

In this particular case, the buffer overflow is by two bytes, and those two bytes are 'k' and NULL, period. While one could certainly imagine a processor architecture in which ASCII (or EBCDIC) 'k' and NULL are valid jump/branch-related opcodes, I defy anyone to come up with even a proof-of-principle exploit for a processor of actual, pragmatic interest. Remote denial of service is the realistic worst case, and while that's certainly bad enough, it's not exactly doomsday. (Last week's issue is similar.)

As to the other two issues, the 2004 one was definitely at the high end of the nasty-o-meter (although I never did hear of any actual exploits--not that I necessarily would have, of course); I no longer recall the specifics of the 2002 one (other than that it had to do with 16-bit samples), but I'll assume remote execution was a possibility there, too. So yes, there have been real issues, and nothing I've said here contradicts any of the main conclusions of the article. I simply wish there could be a little more precision in security advisories, insofar as they're the raw fodder used in so many other analyses...

Greg

P.S. I'll see if I can't get 3334 updated...perhaps I'll get lucky this time.

vulnerability inflation

Posted Nov 23, 2006 19:29 UTC (Thu) by bluefoxicy (guest, #25366) [Link]

Score: 4 (Insightful)

Mod parent up, and other slashdotisms.

I wasn't aware of that issue; although the other listed CVEs are valid I believe. The point was that things like that are stopped cold by PaX/SELinux memory protection policy; and you can't use those with JIT.

vulnerability inflation

Posted Nov 30, 2006 16:05 UTC (Thu) by PaXTeam (subscriber, #24616) [Link]

sorry to burst your security bubble, but things don't quite work the way you imagine it. the 2 byte overflow can very well lead to arbitrary code execution. imagine that the buffer you overflow ends just below the saved frame pointer (whether that actually occurs in real life binaries or not is a question you should have answered before jupming to conclusions about the bug being not exploitable). overwriting the 2 LSBs of the frame pointer with a fixed value of "k\0" will most likely decrease its value (on little-endian CPUs like i386), that is, on function return the frame pointer will point into a memory (stack) region that is not actually part of any live stack frame. depending on the execution history of the whole app (not only lib!) this memory region may very well be valid (mapped) already, hence further code executes with a frame (and later, stack) pointer that isn't quite where it's expected. since past execution may very well have left user controlled data on the stack where the code expects trusted data only, you have a situation wide open for exploitation. this isn't even something new, http://phrack.org/archives/55/P55-08 had desribed this a long time ago.

Virtual Machines and Memory Protections

Posted Nov 30, 2006 14:54 UTC (Thu) by llupus (guest, #20164) [Link]

The restrictions that systems like PaX and SElinux can enforce can prevent any JIT from working, it is not specific to the Mono VM. The people that suggested the use of Java obviously missed the point.

We should also consider that suggesting the use of an interpreter misses the point, too, as an interpreter uses data as opcodes and there is no way to prevent the interpreter from executing opcodes (generated, say, by using a flaw in libpng) using page table permissions.
An interpreter could be considered better here only for the fact that an interpreter is usually simpler than a JIT so it can be more easily audited, but just proposing the use of an interpreter doesn't solve the problem at all.

Also, a needed clarification, since some people seem to be unaware of it:
while C# allows the easy use of native shared libraries, only code that is fully trusted can do so. you could decide to run some programs will less privileges if you think they are security sensitive. The set of permissions and restrictions is also quite flexible, like, you could allow a specific program to access only some environment variables etc.

The article already mentioned that using Mono we could execute most programs without the need of the JIT, by using the AOT mode. Of course some restrictions apply for the mentioned need of Reflection.Emit and also for other features, like marshaling a delegate object as a C function pointer.
The point, though, is that, in security issues, we should try to minimize the risks and still be able to get work done. If you are afraid of being kidnapped, you can lock yourself in your house and never go out of it and have someone else feed you. In most cases in the real world this is not the usual way to protect people, even if it can be effective, because you need to have a balance between security and freedom.
The same happens with virtual machines: we need a balance between security, speed, features, etc.

In this view, the Mono VM, like the JVM, is built to be secure, but trying to avoid the jit is like locking yourself in your house: a miserable life.
This doesn't mean, though, that risks can't and shouldn't be mitigated.
Someone, for example, may want to AOT the code, to minimize the use of the JIT. We could also later implement the code to make the jit code area writable only for the shortest amount of time possible and randomize the positioning of the code. This won't prevent all kinds of attacks, but it reduces the risks while maintaining the feature set intact. This would be a good compromise between features and security concerns. If you really want to feel yourself secure, you'd need to run only code that has been prooved correct, or you can lock yourself at home. Of course feeling secure is not the same as being secure anyway...

One possible solution

Posted Nov 30, 2006 18:43 UTC (Thu) by roc (subscriber, #30627) [Link]

Give the VM executable privileges to execute written memory. On launch, have the VM fork off a separate JIT process, which inherits the privileges, and then drop the special privileges before running the real program. The JIT process communicates with the rest of the VM through two shared memory buffers; an input buffer which is writable by the VM and readable by the JIT, and an output buffer which is writable by the JIT and executable by the VM.

That prevents direct execution of machine code injected by an exploit, but it doesn't prevent a (harder) attack that manages to corrupt bytecode that is then sent to the JIT. But here's a solution to that. Have the JIT process require input bytecode to be digitally signed by a certain authority. Now you can sign all your built-in C#/Java libraries and they will be safely JITted. To handle dynamic code generators, we can restructure them so that the dynamic code generation is performed in the JIT process in response to requests from the main program. If the dynamic code generation is powerful --- e.g., is a form of compilation --- then the dynamic code generator will have to check that the input code is safe. This could be by checking that it's signed by the right authority, or by restricting what the input code can do. E.g. you could have a Scheme compiler that only supports access to the environment via terminal I/O.

As far as I can tell, it's impossible to do better than this.

One possible solution

Posted Dec 1, 2006 2:17 UTC (Fri) by skissane (subscriber, #38675) [Link]

Bravo... I think that is a brillant solution you have proposed.

One possible solution

Posted Dec 4, 2006 16:44 UTC (Mon) by bluefoxicy (guest, #25366) [Link]

I've written a paper about this, but can't seem to solve the problem of deadlocks between native/JIT conflicts and mutexes; or ensure that I can properly track native code.

Picture that native code malloc()'s unmanaged memory; and managed code allocates managed memory. We know what memory managed code is accessing; so we can handle cache synchronization and memory address translation between the processes. Native code, of course, can't be tracked; we have to basically hold all memory in the process running the native code.

The problem comes when one thread locks a mutex, then enters JIT'd code (in the other process); and then a native code path locks the same mutex. The JIT'd code suddenly can't pull memory over (we're assuming native code may be altering any memory, managed or unmanaged); and the native code is waiting for a mutex that won't be open until the JIT'd code is run. These threads deadlock.

Possibly the socket/shm buffer/etc communication could be used, under the assumption that actual memory access timing isn't entirely important. Mutexes are checked/set in native code, so we'll be at the memory storage point; and when mutexes don't block stuff, there's no timing synchronization to worry about.

Oi! The answer's been in front of me the whole time! Your wording gave me the push I needed, thank you!!! :)

http://bluefox.kicks-ass.org/stuff/bluefox/misc/vm_twopro... is the draft copy of the paper, I'll amend it later with uncached remote memory access.

Virtual Machines and Memory Protections

Posted Dec 5, 2006 16:36 UTC (Tue) by tjc (subscriber, #137) [Link]

The C# programming language is gaining popularity [snip]
According to the TIOBE index it's been declining for a while now.

http://www.tiobe.com/tpci.htm

Virtual Machines and Memory Protections

Posted Dec 5, 2006 20:19 UTC (Tue) by zlynx (subscriber, #2285) [Link]

Unlike Java, the C# language isn't identical to the .NET/Mono virtual machine. MS seems to have gone out of their way to make sure many languages are implemented. IronPython is cool. I even played with F# a bit.

I've been seeing more and more video games coming out on Windows that use .NET for their scripting. I don't know if they use C#. They could be using IronPython (python used to be popular for game scripting).

I wonder how many Xbox 360 games are using .NET.

I would think that for game developers embedding .NET is both easier to develop and faster to execute than home-built interpreted (quakeC), compiled (quake2,3,doom3) or bad Java imitations (unreal).

I don't know why more games didn't embed Java in the past. Maybe Sun licensing was a problem.

Anyway, my impression is that the .NET virtual machine is gaining in popularity, whether or not C# is.

Virtual Machines and Memory Protections

Posted Dec 6, 2006 5:06 UTC (Wed) by tjc (subscriber, #137) [Link]

Anyway, my impression is that the .NET virtual machine is gaining in popularity, whether or not C# is.
Yes, that is my impression as well. C# usage may be down slightly, but VB is coming back up. Try to figure that one out.

Copyright © 2006, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds