252 lines
9.3 KiB
Text
252 lines
9.3 KiB
Text
|
|
:experimental:
|
|
|
|
[[sect-Defensive_Coding-Java-Language]]
|
|
== The Core Language
|
|
|
|
Implementations of the Java programming language provide strong
|
|
memory safety, even in the presence of data races in concurrent
|
|
code. This prevents a large range of security vulnerabilities
|
|
from occurring, unless certain low-level features are used; see
|
|
<<sect-Defensive_Coding-Java-LowLevel>>.
|
|
|
|
[[sect-Defensive_Coding-Java-Language-ReadArray]]
|
|
=== Increasing Robustness when Reading Arrays
|
|
|
|
External data formats often include arrays, and the data is
|
|
stored as an integer indicating the number of array elements,
|
|
followed by this number of elements in the file or protocol data
|
|
unit. This length specified can be much larger than what is
|
|
actually available in the data source.
|
|
|
|
To avoid allocating extremely large amounts of data, you can
|
|
allocate a small array initially and grow it as you read more
|
|
data, implementing an exponential growth policy. See the
|
|
`readBytes(InputStream, int)` function in
|
|
<<ex-Defensive_Coding-Java-Language-ReadArray>>.
|
|
|
|
[[ex-Defensive_Coding-Java-Language-ReadArray]]
|
|
.Incrementally reading a byte array
|
|
====
|
|
|
|
[source,java]
|
|
----
|
|
include::example$Java-Language-ReadArray.adoc[]
|
|
|
|
----
|
|
|
|
====
|
|
|
|
When reading data into arrays, hash maps or hash sets, use the
|
|
default constructor and do not specify a size hint. You can
|
|
simply add the elements to the collection as you read them.
|
|
|
|
[[sect-Defensive_Coding-Java-Language-Resources]]
|
|
=== Resource Management
|
|
|
|
Unlike C++, Java does not offer destructors which can deallocate
|
|
resources in a predictable fashion. All resource management has
|
|
to be manual, at the usage site. (Finalizers are generally not
|
|
usable for resource management, especially in high-performance
|
|
code; see <<sect-Defensive_Coding-Java-Language-Finalizers>>.)
|
|
|
|
The first option is the
|
|
`try`-`finally` construct, as
|
|
shown in <<ex-Defensive_Coding-Java-Language-Finally>>.
|
|
The code in the `finally` block should be as short as
|
|
possible and should not throw any exceptions.
|
|
|
|
[[ex-Defensive_Coding-Java-Language-Finally]]
|
|
.Resource management with a `try`-`finally` block
|
|
====
|
|
|
|
[source,java]
|
|
----
|
|
include::example$Java-Finally.adoc[]
|
|
|
|
----
|
|
|
|
====
|
|
|
|
Note that the resource allocation happens
|
|
*outside* the `try` block,
|
|
and that there is no `null` check in the
|
|
`finally` block. (Both are common artifacts
|
|
stemming from IDE code templates.)
|
|
|
|
If the resource object is created freshly and implements the
|
|
`java.lang.AutoCloseable` interface, the code
|
|
in <<ex-Defensive_Coding-Java-Language-TryWithResource>> can be
|
|
used instead. The Java compiler will automatically insert the
|
|
`close()` method call in a synthetic
|
|
`finally` block.
|
|
|
|
[[ex-Defensive_Coding-Java-Language-TryWithResource]]
|
|
.Resource management using the `try`-with-resource construct
|
|
====
|
|
|
|
[source,java]
|
|
----
|
|
include::example$Java-TryWithResource.adoc[]
|
|
|
|
----
|
|
|
|
====
|
|
|
|
To be compatible with the `try`-with-resource
|
|
construct, new classes should name the resource deallocation
|
|
method `close()`, and implement the
|
|
`AutoCloseable` interface (the latter breaking
|
|
backwards compatibility with Java 6). However, using the
|
|
`try`-with-resource construct with objects that
|
|
are not freshly allocated is at best awkward, and an explicit
|
|
`finally` block is usually the better approach.
|
|
|
|
In general, it is best to design the programming interface in
|
|
such a way that resource deallocation methods like
|
|
`close()` cannot throw any (checked or
|
|
unchecked) exceptions, but this should not be a reason to ignore
|
|
any actual error conditions.
|
|
|
|
[[sect-Defensive_Coding-Java-Language-Finalizers]]
|
|
=== Finalizers
|
|
|
|
Finalizers can be used a last-resort approach to free resources
|
|
which would otherwise leak. Finalization is unpredictable,
|
|
costly, and there can be a considerable delay between the last
|
|
reference to an object going away and the execution of the
|
|
finalizer. Generally, manual resource management is required;
|
|
see <<sect-Defensive_Coding-Java-Language-Resources>>.
|
|
|
|
Finalizers should be very short and should only deallocate
|
|
native or other external resources held directly by the object
|
|
being finalized. In general, they must use synchronization:
|
|
Finalization necessarily happens on a separate thread because it is
|
|
inherently concurrent. There can be multiple finalization
|
|
threads, and despite each object being finalized at most once,
|
|
the finalizer must not assume that it has exclusive access to
|
|
the object being finalized (in the `this`
|
|
pointer).
|
|
|
|
Finalizers should not deallocate resources held by other
|
|
objects, especially if those objects have finalizers on their
|
|
own. In particular, it is a very bad idea to define a finalizer
|
|
just to invoke the resource deallocation method of another object,
|
|
or overwrite some pointer fields.
|
|
|
|
Finalizers are not guaranteed to run at all. For instance, the
|
|
virtual machine (or the machine underneath) might crash,
|
|
preventing their execution.
|
|
|
|
Objects with finalizers are garbage-collected much later than
|
|
objects without them, so using finalizers to zero out key
|
|
material (to reduce its undecrypted lifetime in memory) may have
|
|
the opposite effect, keeping objects around for much longer and
|
|
prevent them from being overwritten in the normal course of
|
|
program execution.
|
|
|
|
For the same reason, code which allocates objects with
|
|
finalizers at a high rate will eventually fail (likely with a
|
|
`java.lang.OutOfMemoryError` exception) because
|
|
the virtual machine has finite resources for keeping track of
|
|
objects pending finalization. To deal with that, it may be
|
|
necessary to recycle objects with finalizers.
|
|
|
|
The remarks in this section apply to finalizers which are
|
|
implemented by overriding the `finalize()`
|
|
method, and to custom finalization using reference queues.
|
|
|
|
[[sect-Defensive_Coding-Java-Language-Exceptions]]
|
|
=== Recovering from Exceptions and Errors
|
|
|
|
Java exceptions come in three kinds, all ultimately deriving
|
|
from `java.lang.Throwable`:
|
|
|
|
* *Run-time exceptions* do not have to be
|
|
declared explicitly and can be explicitly thrown from any
|
|
code, by calling code which throws them, or by triggering an
|
|
error condition at run time, like division by zero, or an
|
|
attempt at an out-of-bounds array access. These exceptions
|
|
derive from from the
|
|
`java.lang.RuntimeException` class (perhaps
|
|
indirectly).
|
|
|
|
* *Checked exceptions* have to be declared
|
|
explicitly by functions that throw or propagate them. They
|
|
are similar to run-time exceptions in other regards, except
|
|
that there is no language construct to throw them (except
|
|
the `throw` statement itself). Checked
|
|
exceptions are only present at the Java language level and
|
|
are only enforced at compile time. At run time, the virtual
|
|
machine does not know about them and permits throwing
|
|
exceptions from any code. Checked exceptions must derive
|
|
(perhaps indirectly) from the
|
|
`java.lang.Exception` class, but not from
|
|
`java.lang.RuntimeException`.
|
|
|
|
* *Errors* are exceptions which typically
|
|
reflect serious error conditions. They can be thrown at any
|
|
point in the program, and do not have to be declared (unlike
|
|
checked exceptions). In general, it is not possible to
|
|
recover from such errors; more on that below, in <<sect-Defensive_Coding-Java-Language-Exceptions-Errors>>.
|
|
Error classes derive (perhaps indirectly) from
|
|
`java.lang.Error`, or from
|
|
`java.lang.Throwable`, but not from
|
|
`java.lang.Exception`.
|
|
|
|
The general expectation is that run-time errors are avoided by
|
|
careful programming (e.g., not dividing by zero). Checked
|
|
exception are expected to be caught as they happen (e.g., when
|
|
an input file is unexpectedly missing). Errors are impossible
|
|
to predict and can happen at any point and reflect that
|
|
something went wrong beyond all expectations.
|
|
|
|
[[sect-Defensive_Coding-Java-Language-Exceptions-Errors]]
|
|
==== The Difficulty of Catching Errors
|
|
|
|
Errors (that is, exceptions which do not (indirectly) derive
|
|
from `java.lang.Exception`), have the
|
|
peculiar property that catching them is problematic. There
|
|
are several reasons for this:
|
|
|
|
* The error reflects a failed consistenty check, for example,
|
|
`java.lang.AssertionError`.
|
|
|
|
* The error can happen at any point, resulting in
|
|
inconsistencies due to half-updated objects. Examples are
|
|
`java.lang.ThreadDeath`,
|
|
`java.lang.OutOfMemoryError` and
|
|
`java.lang.StackOverflowError`.
|
|
|
|
* The error indicates that virtual machine failed to provide
|
|
some semantic guarantees by the Java programming language.
|
|
`java.lang.ExceptionInInitializerError`
|
|
is an example—it can leave behind a half-initialized
|
|
class.
|
|
|
|
In general, if an error is thrown, the virtual machine should
|
|
be restarted as soon as possible because it is in an
|
|
inconsistent state. Continuing running as before can have
|
|
unexpected consequences. However, there are legitimate
|
|
reasons for catching errors because not doing so leads to even
|
|
greater problems.
|
|
|
|
Code should be written in a way that avoids triggering errors.
|
|
See <<sect-Defensive_Coding-Java-Language-ReadArray>>
|
|
for an example.
|
|
|
|
It is usually necessary to log errors. Otherwise, no trace of
|
|
the problem might be left anywhere, making it very difficult
|
|
to diagnose related failures. Consequently, if you catch
|
|
`java.lang.Exception` to log and suppress all
|
|
unexpected exceptions (for example, in a request dispatching
|
|
loop), you should consider switching to
|
|
`java.lang.Throwable` instead, to also cover
|
|
errors.
|
|
|
|
The other reason mainly applies to such request dispatching
|
|
loops: If you do not catch errors, the loop stops looping,
|
|
resulting in a denial of service.
|
|
|
|
However, if possible, catching errors should be coupled with a
|
|
way to signal the requirement of a virtual machine restart.
|