Dead Code

Dead Code is any code that is never executed, or if executed, the execution has no effect on the application's behavior.

Lines of code are either not executed or redundant because they

  • are no longer needed,

  • are not called,

  • cannot be called.

Further, Dead Code can be also represented by objects not really used during run-time.

Dead code has a few possible definitions. Sources:

Boroumand, Amir. 2018. "The Risks of Dead Code." Dev.to, August 8. Accessed 2021-04-19

Carnegie Mellon University. 2021. "MSC12-C. Detect and remove code that has no effect or is never executed." SEI CERT C Coding Standard, Carnegie Mellon University, April 20. Accessed 2021-04-20.

Refactoring.Guru. 2020. "Dead Code." Refactoring.Guru, January 1. Accessed 2021-04-19

Romano, Simone, Christopher Vendome, Giuseppe Scanniello, and Denys Poshyvanyk. 2020. "A Multi-Study Investigation Into Dead Code." IEEE Transactions on Software Engineering, vol. 46, no. 1, pp. 71-99, January. doi: 10.1109/TSE.2018.2842781. Accessed 2021-04-19

Tobias. 2017. "Finding Dead Code." Blog, November 25. Accessed 2021-04-19

Linders, Ben. 2021. "Dead Code Must Be Removed." InfoQ, February 9. Accessed 2021-04-19

Dead code usually does not get enough attention, as dev teams have to deal with lots of other problems related to functioning and execution. Dead Code isn't breaking anything, so why fix it?

Contents

Potential Security Threats

Dead Code is a quality issue that might indirectly make it easier to introduce security-relevant weaknesses. On the Security point-of-view it is also called Zombie Code or Dormant Code. If you’re an architect or developer looking at your code base, you’ll generally spend more time with the classes that you’re actually working on, not the other 10 million lines of code in your legacy monolith.

Zombie Code

The nature of Zombie Code is that unless something breaks at compile time, most developers working on a project would have no clue that this code even exists. The inability to have insights into your complete code base is a risk not only to productivity but also Security.

Aside from famous data breaches on companies like Equifax and Yahoo and Knight Capitals, recently a group called Elephant Beetle figured out a way to exploit legacy Java apps to the tune of millions of dollars. So it’s well understood that legacy technologies present an opportunity for cyberattacks.

This is where Technical Debt rears its ugly head: The Dead Code in your code base is still being scanned with the same tools, but it’s not being maintained in the same way. Processes and best practices that were initiated five or 10 years ago when the code was written are unlikely to be the same in place today.

So if you’re not looking at this accumulation of Dead Code, who else might be? The Security threat here is that Dead Code isn’t affecting your regular users. This means that any bad actor that tries to find it can potentially be able to exploit it using legacy vulnerabilities. On exploit example is injecting snippets of self-propagating malware code by replacing the Dead Code, using Instruction substitution techniques.

Performances

Dead Code harms the performance of your application. An unused variable here or there will be negligible, but as applications grow in scale and sophistication, code optimization becomes increasingly important. Removing Dead Code that eats up runtime and memory is the easiest way to improve performance because it can be deleted and the application's functionality won't be impacted.

Code Complexity

Dead Code also makes your source code unnecessarily complex. This makes the code harder to maintain and build upon. When a developer wants to make a change to the codebase, he/she has to understand what the existing code is doing. It can take a long time to determine that some code is dead, especially as the program size increases. This is all time that could've been saved if that Dead Code had been cleaned out before.

Susceptibility to Errors

Another major disadvantage of dead snippets of code is that the interactions and errors they cause must be taken into account even though the affected lines are no longer executed at all. For example, an unused code, for being included in the production package, can bring potentially dangerous implicit dependencies to old software or old libraries' versions, contributing to the application run-time instability and creating compatibility problems with the target system. Further, the application can be exposed to Dependency Confusion Attacks.

The removal of dead lines of code greatly reduces the susceptibility to errors. It also puts developers in the comfortable position of not having to deal with compatibility issues and compilation problems as much.

Main Causes of Dead Code

The biggest cause of Dead Code is that it is risky to remove code. No developer wants to be the one to crash the entire app by deleting that one line that actually was needed. Even if something looks like Dead Code, it's hard to say for sure.

There are other reasons why Dead Code arises and goes unnoticed. Often there is simply a lack of the necessary know-how to correctly assess the service life of code.

Another reason that Dead Code persists is that sometimes it is assumed that the old snippets that don't serve any purpose can be used again later. Instead of keeping the old code snippets, it is better to use version control in such cases. It is important to note, however, that code that has been deleted is rarely referenced.

Dead Code and Technical Debt

Because Dead Code is a major source of Technical Debt. A small amount of Technical Debt is inevitable and is perhaps even healthy because it indicates that your team is moving quickly. But like financial debt, Technical Debt compounds really quickly. Seemingly overnight, your team's progress can halted in its tracks because you struggling to navigate through all the tech debt you've accumulated.

Compliance Standards

Solving Dead Code is especially important when complying with industry standards such as DO 178 B/C, ISO 26262, IEC 61508, and IEC 62304.

Our solution is important as it reduces time spent by engineers to get to 100% code coverage, and it saves time in testing and development resources. This is particularly true for high-integrity and safety-critical applications that must adhere to standards like DO 178 B/C.

DO-178B/ED-12B defines dead and deactivated code as follows:

  • Dead Code - Executable object code (or data) which, as a result of a design error, cannot be executed (code) or used (data) in an operational configuration of the target computer environment and is not traceable to a system or software requirement. An exception is embedded identifiers.

  • Deactivated Code - Executable object code (or data) which by design is either (a) not intended to be executed (code) or used (data), for example, a part of a previously developed software component; or (b) is only executed (code) or used (data) in certain configurations of the target computer environment, for example, code that is enabled by a hardware pin selection or software programmed options.

DO-178B/ED-12B essentially requires that any Dead Code is removed, and it calls for the verification of Deactivated Code to prove that it cannot be inadvertently activated. Because of this, the cost of testing is very high. Identifying Dead Code is also a good development practice irrespective of certification requirements because studies have shown that Dead Code and Deactivated Code are a source of hidden defects and run-time errors.

Related Guidelines

SEI CERT C++ Coding Standard

VOID MSC07-CPP. Detect and remove dead code

ISO/IEC TR 24772

Unspecified functionality [BVQ]
Dead and deactivated code [XYQ]

MISRA C:2012

Rule 2.1 (required)

MITRE CWE

CWE-561, Dead code

Legal

The presence of Dead Code in your application can lead to serious consequences as it can be a source of hidden bugs. Dead Code also implies that the code is inefficient as it can increase the size of your executable, which is especially relevant for embedded applications. There can also be legal implications and uninteded consequences.

Reasons to DCE

Here are some reasons why unused code should be removed (DCE-Dead Code Elimination):

  • For anyone new working on a project, they not only have to understand the working code, they have to understand unused material also. This is wasted time and creates confusion.

  • There is a danger that at some time, someone will make a change which inadvertently involve the Dormant' Code and can introduce bugs. I know it's happened on projects you've worked on.

  • The maintenance of any code is an administrative burden. By preserving old redundant code that burden is increased. For example, merging changes in the main branch becomes harder because there is more code to work through and more possibility to make a mistake.

  • What happens over time is that more and more old unused code is added to the codebase. This increases the confusion, potential misunderstanding and administrative overhead.

  • The chances that the unused code will ever be used again is very unlikely. With time that possibility of re-use diminishes. If code is to be removed and is considered important enough then the code can be branched off and documented.

  • Any personal feelings that a coder may have about code they may have worked hard on are understandable. But part of being professional requires that those thoughts have to be put to one side for the better good. Time stands for no-one and there is no place for preserving historical code in a working codebase.

Existing Approaches

Dynamic analysis tools work by running the program to see which lines are executed. Run-time tools can also be used in such a way that they check which modules are used and which are not during normal operation. There is no need to search the standard libraries or installed packages to find Dead Code in your project with such tools. A number of open-source tools are available for detecting Dead Code. Unfortunately, most of them are specific to a programming language or use a bad approach.

A bad approach to Dead Code detection is a run-time analysis that logs (or instruments) all calls when the program executes. After execution, a list of the unused parts is built. While this appears to produce a Dead Code list, it only produces a list of potentially one, with partial code coverage.

The program may not have executed all the lines under all possible conditions. Thus, some live code was most probably not executed and the Dead Code list includes code that executes during another run. What is more, compile-time requirements are not detected. Such requirements can be the use of constants, user-defined data types, class data types and interfaces. Run-time analysis has its uses in performance optimization and testing, but it's not perfect for Dead Code detection.

Yet another, but almost as bad, approach to automated Dead Code detection is programmatic reading of the compiled files: the executable files or the intermediate code (byte code, MSIL). Again, compile-time requirements are not detected. What is more, compiled files contain less information than the source code and are not enough without source code comparing. Only the source code provides enough detail for reliable and complete Dead Code detection.

Our Approach

Our approach is programming language indipendent, compliant with CERT, MISRA and CWE, and solves all the above problems by correlating source code analysis and sandboxing compiled (Binary / Bytecode) analysis, with the results of 100% Dead Code and Deactivated Code detection. This saves time and reduces the cost associated with testing activities geared for robustness and code coverage.

Static Reviewer, thanks to its proprietary Dynamic Syntax Tree algorithm, analyzes on-the-fly your Application/Program, both statically (source code) and dynamically (compiled code) . All possible Application Paths will be trespassed by sandboxing the compiled parts and comparing it with related source, even in case of optimzed compilation or executable shrinking. All functions will be stimulated and all branches will be executed, event-driven code and error handling parts included.

This automatic process will discover 100% of Dead Code, including:

  • Unused objects/variables/fields/attributes or initialized-only (dead assignment)

  • Unused return Values

  • Never executed code blocks

  • Empty code blocks

  • Empty Event Handler

  • Redundant Code

  • Unused Classes and Functions

  • Unused function Parameters

  • Unattainable code. It occurs when lines of code cannot be called, e.g. because a condition of another line, which always occurs, prevents this

  • Unnecessary Conditions

  • Unreachable Code

  • Dead Return Value. A return value which is not used by any other function

  • Dead Event Declaration. An event that does not trigger any action, is a dead event

  • Test/Debug or other non-functional Code

  • Reusable components that are designed for different run-time scenarios

It does not require additional software installation.

 

COPYRIGHT (C) 2015-2024 SECURITY REVIEWER SRL. ALL RIGHTS RESERVED.