I have a Java application that calls lots of different native methods of a legacy application through JNI. But JVM crashes with a stack dump at random places, outside any JNI call. Sometimes it crashes during GC, sometimes during class loading and other places. I suspect that one or more native methods is corrupting JVM heap or some other data structure. I need to know which call is this, so I can fix the native implementation.
The legacy application is a 3rd party DLL for which I don't have sources nor symbol information. To make it callable from Java, I built a wrapper DLL that uses JNI calling conventions.
The perfect solution would be an extended JVM option that forces JVM to automatically check integrity of heap and its other data structures after each JNI call.
Do you know of something that can help?
P.S. Please don't tell me to build a socket or pipe layer between JVM and the legacy application, because our requirements disallow that. This is about bug detection, not architecture design.
Because I went out of answers and couldn't find a ready solution by myself, I ended up building a sandbox process in pure C++ just to identify the problem. My Java app instantiates the sandbox process using ProcessBuilder and then communicates with it using stdin and stdout. Instead of JVM, it's the sandbox who actually loads and calls the legacy DLL. Then I monitored the sandbox process using Microsoft's Application Verifier, which found a memory corruption problem - there was a call passing a buffer smaller than expected. After this was identified, I just increased the length of byte[] used as buffer in the Java app, and now JVM can make direct calls to DLL without use of sandbox.
Overall, I lost almost 10 days just because JVM doesn't have an option to verify heap after each JNI call. But at least now if someone finds a crash we can quickly debug it using the sandbox.
Related
I'm extending a server application written in Java to allow user-defined callbacks (written in Javascript) to be run in response to requests. I've done some reading, and while it seems possible to disable Java classes in Nashorn, there is nothing stopping a user from creating Javascript code that allocates an enormous array without using any Java APIs. I'm wondering if there is any way to restrict this, either proactively or reactively.
The solution I came up with is to have a process pool of JVMs with small max heap sizes, which are responsible for running the user-defined code. There will be a worker pool manager to spawn new processes when needed. This way, the main process, as well as other user-defined code, will not be affected by a single malicious user. While this solution will likely work, it seems heavy-handed. Is there no better solution for preventing malicious users from using too much memory?
I'm not particularly set on Javascript, so if there exists any other scripting language that can be run within a JVM and also has support for memory usage limits, I would be open to using it instead of Nashorn. Unfortunately, it seems like Jython, JRuby, and LuaJava all don't have what I'm looking for. Thanks in advance.
Is there a way to run some java byte-code into a specially restricted part of a running JVM ? I'm thinking about access to very little ram (a few tens of kilobytes perhaps) and no access to the external world whatsoever (apart from that ram).
The goal would be to execute some user provided byte-code into this safe environment in a way that the host cannot ever crash or leak information from the execution of rogue byte-code.
You can run untrusted bytecodes within a security sandbox, and setup the sandbox so that there is no possibility of communicating with the outside world. This is what a browser-resident JVM does when you run an untrusted applet ... except that you need the sandbox restrictions to be tighter. (An applet sandbox doesn't block ALL network connections.)
Reference: How do I create a Java sandbox?
However, it is NOT POSSIBLE to entirely control what the rogue code does. For example, if it decides to go into an infinite loop or allocate a huge data structure, the trusted part of your JVM has no bomb-proof way of stopping it. And if there is a security flaw in the JVM, class libraries or your sandbox, then there's a chance that the rogue code could exploit it.
Note that none of this involves restricting the code to a particular area of RAM. You can't do that in Java.
You could use JavaPathfinder (JPF) for this type of exercise. JPF is a model checking tool that takes a source-code/byte-code and executes it in its own virtual machine, you can define various properties (deadlock-free, infinite loops, etc.) to check for.
JPF operates as a self-standing tool so it would be hard to integrate it in your application but perhaps you could call it externally and then just query for results.
I understand that Java can load/execute DLL code, but I'm wondering if there are any security checks to prevent untrusted code from the system being called by a JVM. Couldn't this destroy the system -- are there any OS features that prevent this? Or can someone just write in Java itself some method that prevents untrusted code from being loaded? Thanks for your help.
No. Once you call out to native code (via JNI) then that native code is free to do anything (subject to the OS itself giving permission). There's no concept of sandboxing the native code invoked from the JVM.
Note that this is a particular headache with JNI code. Badly coded native code can take down the JVM (as opposed to simply throwing an exception) and the consequent debugging/resolution is particularly hard.
The loading of native code can itself be prevented. Typically e.g. applets run such security context that they cannot load native libraries. However, if the JVM lets your Java code call into untrusted native code, all bets are off.
Some modern network cards support Direct Memory Access for improved performance. How can I utilize this feature from Java?
Does the JVM provide this automatically, or do I need to do an allocateDirect on the ByteBuffers that I am using to talk to that NIC?
Does anyone have documentation that discusses this?
It is the operating systems task to use the DMA feature of the network card. The JVM does not really care how the OS does it, and simply uses the operating system's functions for talking to "network interfaces".
You cannot do this from inside Java in the typical desktop/server JVMs, as this is operating system area which requires you to reach out into C code. Go have a look on JNI or JNA to see how to do this. Please note that this may make your application brittle if you do not get this exactly right.
Yeah - ankon's answer is right. Java operates in a sandbox - a virtual machine (hence the, "VM" in JVM; Sun actually built ONE physical version -- it's on display somewhere).
Java was never designed (intentionally) to reach outside the sandbox, unlike ActiveX, which can go just about anywhere on a PC.
Just think of all the bad things ActiveX has done over the years via a browser. You wouldn't want that to happen with Java, would you?
Although...
you might be able to instantiate an object in Java that does have access to the hardware (like one of those ActiveX controls, or some DLL, for example - which you'd have to write, too).
The problem I see is the throughput. With 100MB or 1000MB cards, would a JVM (remember, this is a VM running on an OS, so you're a couple of layers removed from the hardware) have the speed to handle what's coming in under load? Would you want a Java program holding up data in your NIC while it tinkered with it (think of the impact to the rest of the system)?
At this point, you're probably better off writing the hard-working guts of your solution in C. And, if you still need Java to play with that data, put it in a place where Java can get to it.
If you're not getting the network throughput you need in java, then you're going to need to write a C wrapper in order to access it.
Have you benchmarked your code to find where your performance issues really are? If you let us know that we can likely help you out without resorting to JNI.
I have a java application which uses JNI in some parts to do some work. It follows the usual loading of DLL and then calling native methods of DLL. Is there any way we can restrict what native methods can do from the java application? For example, can we restrict DLLs not to open any files or not to open any sockets even if it has the code to do it? It can just forbid DLLs it loads for doing certain things, may be by loggin something or throwing an exception.
No you can't. The DLL gets loaded as a whole and then the Java side has no control on what the native code is doing.
One solution might be kind of man in the middle approach. This would involve coding a "shell" DLL that has the same interface as the original DLL. You tell Java to load a "shell" DLL for instance by putting it in a specific location and using the java.library.path property. Then the role of the "shell" DLL is to load the "true" DLL by sandboxing it and redirecting standard functions. This sounds like a lot of pain and this something that would happen in the native side on things, not from Java.
Edit 2021: today it's also relevant to point out that the sandbox to run Java in would likely be a virtual machine, in the cloud, Docker or what have you, in a locked down configuration.
I liked Gregory Pakosz' answer a lot. However, what you could do is sandbox the Java instance itself. Start the Java application itself in a restricted context.
In Windows or Unix you can create a user which is limited to a certain directory and only has access to some DLLs. Thus the DLL called from JNI can do whatever it wants, but it will not get very far, because the user the Java runs as can not do very much.
If your Java program needs to do privileged things, the Java side of it will have to talk to another program (Java or not) to do its' privileged things for it.
Just keep in mind, that if you can not trust the DLL, you can no longer trust the Java code either, since the DLL might have "hacked" the Java machine. On the other hand, no nasty stuff should be able to break out of the limits of the user they run as. (Barring misconfiguration or a bug in the OS.)
Normally you would run your application under the Java security Manager but I don't believe it has any effect on code running through the JNI.
You could implement some kind of setting that your JNI code could get. For example, on an UNIX system, you could create groups for special types of privileges, and check if the current user has the required privileges, else just return 0 or something.