Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Tuesday, January 28, 2014

Stack allocation for Java

In my previous post, I mentioned how useful stack allocation would be for Java, and expressed my surprise, bordering on exasperation, for why it is not available.

Since then, I've thought about how such a thing might work, and realized it is impossible to include without either breaking Java's security model, or throwing away any benefits through additional checks.

First, let me describe a possible implementation: We define a new stacknew operator which acts just like the new operator, except that it returns a reference to an object allocated in the current stack frame, instead of on the heap.

Now for a few design choices, what I would choose, and why:

  1. During construction, should all instances of new act like stacknew? No, They should act like normal new. Although it might be convenient for such an automatic conversion to stacknew during construction, it would likely cause problems:
    1. It would encourage the use of stacknew to construct objects not intended for stack-frame lifetime, through behaviors like saving references to themselves in other objects.
    2. It would bring up the question of converting all new instances to stacknew when executed by the object, which would be impossible to get right. Should we convert method code? Static method code? Inherited code from superclasses? Taint all calls made by the object to other objects? It is too messy and not predictable.
    3. Therefore, stacknew must be intended for use on classes specifically designed for it.
  2. Should it be possible to determine if the this object was allocated with stacknew? Yes. This will allow special-purpose code that is stacknew-aware to modify its behavior, using calls to stacknew instead of new when it is stack-allocated. Otherwise it would be difficult to write a general-purpose class without passing around a boolean flag indicating which new operator to use. In fact, Object should have a new method, isStackAllocated(), probably public.
  3. What should happen when dereferencing a reference to a stack-allocated object whose stack frame returned? This should generate a NullPointerException, or similar. Maybe a new Error subclass would be called for. And this is where the trouble starts...

To fit with Java's model of simply disallowing any sort of unsafe memory access, it must not be possible to successfully dereference a reference to a stack-allocated object whose frame has returned. Well, that doesn't sound so hard! I hear people thinking. But it is hard. Surprisingly so.

So how do you know, when you try to dereference a pointer, whether it points to an allocation in an invalid stack frame? You cannot depend on any property of the memory in that frame. Here are the possible scenarios when trying to perform an access:

  1. The object is still there and fine. This is what you would expect during a dereference operation.
  2. The frame returned, but no new frame has overwritten that part of the stack yet. This could be considered lucky, but it would be a nasty bug to fix if something changed.
  3. The frame returned, and a smaller function overwrote part of the memory but not all of it. This would likely produce some very indeterminate behavior; what if you grabbed a reference out of the corrupted object and then tried to dereference that? Some way be valid while others are not.
  4. The frame returned, and other functions completely overwrote the object.

So how do we protect against this? As I said, we cannot trust any of the object's memory. Here is the solution I came up with:

  1. Each stack frame containing any stack-allocated object will have a non-zero cryptographically secure identifier (random number) at a known offset from the beginning of the stack frame.
  2. Every reference will have a type, either heapnew or stacknew. Probably a bit flag somewhere.
  3. Every stacknew reference will contain, in addition to the object pointer, a pointer to the beginning of the stack frame and the value of the stack frame's identifier.
  4. On every dereference, the JVM will check if it is a stacknew reference. If so, it will first verify that the frame still exists before finishing the dereference operation. If not, it will throw an exception or error. This verification is performed by matching the frame identifier in the reference to the value expected by looking at the location where the identifier would be if the frame was still alive.
  5. Any time a stack frame returns, the identifier field in the frame is set to zero.

Why cryptographically secure? Because otherwise it would be theoretically possible for an attacker to guess what the identifier of a frame might be, and arrange to have a specific memory layout occur, resulting in successfully dereferencing a malicious pointer. This scheme makes it vanishingly unlikely to have a false positive match, malicious or otherwise.

I am reasonably confident that this solution will prevent accidental dereferencing of invalid memory, but it does not sound at all efficient, especially when you account for references needing to be three times their current size.

So, to conclude this surprisingly long-winded post, I am saddened to say that I seriously doubt Java could support a stacknew operator.

But that doesn't stop me from wanting one...

Thursday, January 9, 2014

C++ vs. Java

I'm not trying to start any sort of religious war here; I'm writing this because I've been programming in C++ for the past six or seven months after more than three years of professional Java (and more than ten years in personal projects). So I feel like I have an informed opinion.

Unfortunately, my opinion may incense some: Programming in C++ after being used to Java is like slogging through mud. And here's why:

  • Auto-completion. Eclipse does it badly for C++, and neither Emacs nor Vim will do it automatically. I'm sure I could set it up, but it still would not be as good at it as Java in Eclipse. I am unaware of other choices (don't even try to suggest Visual Studio; nobody uses Windows for real software development).
  • Header files. I need to write one of these, with ancient C include guards around it, for almost every interesting piece of code.
  • Prototypes. I need to repeat myself. I need to repeat myself. It's 2014!
  • Surprisingly, flexibility of where classes can be written. In Java, if I want to write a new class, it pretty much always gets its own new file, and the name of the file is the name of the class. But in C++? It can go anywhere, including in whatever file I'm writing when I decide I want it. And there is no naming requirement for linking headers with source files.
  • Threading is a special case in C++.
  • Memory management. I wish there were more options in Java (stack allocation, for example), but it requires too much extra effort in C++, including destructors. Rust has some interesting ideas around this, but it adds to the number of indirect reference types.
  • Boilerplate code. Java has some, certainly, but C++ has so much more. Refer back to header files and prototypes. And memory management (std::unique_ptr<actualtype> anyone?). And how many times have you written "virtual ~ClassName() {}"? Or, even worse, "virtual ~ClassName();" in a header and "ClassName::~ClassName() {}" (with its multiple instances of repetition) in a source file?
  • Namespaces. Not only do I need to include something, but I still need to either type out the namespace every time, or add a using directive. And then we get back to problems with header files; can't put a using directive there without screwing everything up when someone includes the header. Oh, and they're not implicit based on the file path, so at a minimum I'll need to write each new one twice. Is it obvious yet how much I hate to repeat myself?

But, to be fair, there are a few areas where Java is severely lacking:

  • Memory  management. It is really hard to write a no-gc Java program, and it may be impossible without abusing class fields and sacrificing immutable types. Stack allocation would go a long way here. Seriously. Why is this still missing? You could probably do it with a single new keyword, or by overloading the meaning of one of the operators.
  • Const. I really miss this in Java. Really, really </Shrek voice>.
  • Templates. C++ mostly got this right with weak typing. Java requires strong typing, and you can't really specialize. And they're erased, so you're doubly... Triply screwed.
  • Macros and conditional compilation. Less important (to me) than anything else here, but sometimes I really wish I could define a macro that would evaluate into some repetitive multi-line construct that, instead, I need to type out (usually with the help of block-selection and editor macros). Oh, look at that, more repetition...

It's entirely possible that most of this boils down to how expressive the language is. Either way, maybe it's time to learn Go...

Monday, January 26, 2009

Log4j

Recently I started a new project at work. It is a server-side program suite for managing a large number of devices out in the world, with a database back-end and all sorts of goodies. I decided that it would be a good idea to use a standard logging package, instead of my standard method.

Not that my standard method is bad, it just isn't very flexible. There is a program-wide DEBUG flag set at startup which controls all debugging output of any sort, from any source. Since I'd be working with a much larger system than I'm used to, I thought it'd be nice to use a package with a few more features than 'debugging output is on' and 'debugging output is off.'

So Log4j seemed like the best choice. It's open-source (from the Apache foundation) and provides all of the neat little features that might be useful:

  1. It can selectively turn on/off logging depending on the source of the call.
  2. It can direct logging output to any of a number of different destinations.
  3. It has importance levels, so you can filter out the debugging messages from stable code but continue to see the errors/warnings from the same object.

Sounds pretty good, huh? It certainly did to me. But if that's the case, why am I writing this, you might ask? If you're a loyal reader, you may have noticed that I don't have many positive posts...

Log4j has virtually no documentation. Well... no, let me revise that: Log4j has virtually no free documentation.

There's a short introduction describing what it can do, how fast it is, ... BUT it doesn't tell you how to do it.

There's a FAQ which tells you what it can do, how fast it is, ... but again, not how to do it. It just gives you more details on the same subjects as the introduction.

There's even a Wiki! But no help there, it's aimed at developers of Log4j, not developers of programs using Log4j.

So where can I learn how to use this feature-rich, fast logging platform for Java? Well, you can buy a $20 PDF book that describes everything!

But surely there's information available online, through Google or something, right? No. Everybody just seems to know how to use it, but nobody is saying how they know. Did everybody read the source code? I don't know... I certainly don't want to learn how to use a library by reading its source, that's what documentation is for!

Oh, there are some examples on the Log4j page, and a few more spread around the web, but guess what? They're the same examples! Nobody thought that it would be a good idea to, perhaps, come up with their own to describe the other features that aren't documented on the home page. They just copied the same examples and assumed they explained everything.

So here are a few little pieces to help the lost developer trying to configure Log4j that doesn't want to buy the book or read through the source:

Other useful things you can do are use a rolling file appender (class is 'org.apache.log4j.RollingFileAppender'), which is given a maximum file size. It will output all messages to that file until it hits the max size, and then rename it and create a new one to write to. Here are the properties you should know about with it:

  • log4j.appender.RFA.File=filename.log
  • log4j.appender.RFA.MaxFileSize=1M
  • log4j.appender.RFA.MaxBackupIndex=3

This will append to filename.log until it reaches 1 megabyte, then it will rename it to filename.log.1 (renaming filename.log.1 to filename.log.2 and so on as necessary), keeping files up to filename.log.3, but no more.

You can specify multiple appenders for the root logger like this:

 log4j.rootCategory=ALL, Console, RFA

One more useful trick is to send logger output to specific places. You can do that like this:

 log4j.logger.loggerName=ALL, A2
 log4j.additivity.loggerName=false

That will cause all logger output from that specific logger to go to A2, but not to the root logger's appender.

And finally, to get Log4j working, you need to add a little bit of code to the initialization of your program. I'd suggest doing this in a static block in your main class. You just need to call

 PropertyConfigurator.configureAndWatch(
     "log4j.properties", 5000)

That will cause the properties file that you just wrote using all of the tips I gave to be loaded at startup, and it will be checked for updates every 5 seconds. If it changes, the new settings will go into effect. That allows you to selectively enable logging as you notice unexpected behavior, but not be overwhelmed by log messages all the time.

So what's my conclusion? I know, I jumped from complaining about Log4j to showing how it works and what you can do with it. I actually kind of like the system. It is convenient to work with (except when you need to configure something), it monitors the configuration file so I can enable logging after the program starts, and I can supress messages for parts of the program I've already finished debugging.

So I'd say that you should give it a try, and hopefully someone will help you out when you can't find the right option for your current situation. And if nothing else works, read the source. It's painful, but that's how I figured out the additivity options.