Unintended Reentrant Invocation of Non-reentrant Code Via Nested Calls

During execution of non-reentrant code, the product performs a call that unintentionally produces a nested invocation of the non-reentrant code.


Description

In a complex product, a single function call may lead to many different possible code paths, some of which may involve deeply nested calls. It may be difficult to foresee all possible code paths that could emanate from a given function call. In some systems, an external actor can manipulate inputs to the system and thereby achieve a wide range of possible control flows. This is frequently a concern in products that execute scripts from untrusted sources. Examples of such products are web browsers and PDF readers. A weakness is present when one of the possible code paths resulting from a function call alters program state that the original caller assumes to be unchanged during the call.

Demonstrations

The following examples help to illustrate the nature of this weakness and describe methods or techniques which can be used to mitigate the risk.

Note that the examples here are by no means exhaustive and any given weakness may have many subtle varieties, each of which may require different detection methods or runtime controls.

Example One

The implementation of the Widget class in the following C++ code is an example of code that is not designed to be reentrant. If an invocation of a method of Widget inadvertently produces a second nested invocation of a method of Widget, then data member backgroundImage may unexpectedly change during execution of the outer call.

class Widget
{
  private:
    Image* backgroundImage;

  public:
    void click()
    {
      if (backgroundImage)
      {
        backgroundImage->click();
      }
    }

    void changeBackgroundImage(Image* newImage)
    {
      if (backgroundImage)
      {
        delete backgroundImage;
      }
      backgroundImage = newImage;
    }

}

class Image
{
  public:
    void click()
    {
      scriptEngine->fireOnImageClick();
      /* perform some operations using "this" pointer */
    }

}

Looking closer at this example, Widget::click() calls backgroundImage->click(), which in turn calls scriptEngine->fireOnImageClick(). The code within fireOnImageClick() invokes the appropriate script handler routine as defined by the document being rendered. In this scenario this script routine is supplied by an adversary and this malicious script makes a call to Widget::changeBackgroundImage(), deleting the Image object pointed to by backgroundImage. When control returns to Image::click, the function's backgroundImage "this" pointer (which is the former value of backgroundImage) is a dangling pointer. The root of this weakness is that while one operation on Widget (click) is in the midst of executing, a second operation on the Widget object may be invoked (in this case, the second invocation is a call to different method, namely changeBackgroundImage) that modifies the non-local variable.

Example Two

This is another example of C++ code that is not designed to be reentrant.

class Request
{
  private:
    std::string uri;
    /* ... */

  public:
    void setup(ScriptObject* _uri)
    {
      this->uri = scriptEngine->coerceToString(_uri);
      /* ... */
    }

    void send(ScriptObject* _data)
    {
      Credentials credentials = GetCredentials(uri);
      std::string data = scriptEngine->coerceToString(_data);
      doSend(uri, credentials, data);
    }

}

The expected order of operations is a call to Request::setup(), followed by a call to Request::send(). Request::send() calls scriptEngine->coerceToString(_data) to coerce a script-provided parameter into a string. This operation may produce script execution. For example, if the script language is ECMAScript, arbitrary script execution may result if _data is an adversary-supplied ECMAScript object having a custom toString method. If the adversary's script makes a new call to Request::setup, then when control returns to Request::send, the field uri and the local variable credentials will no longer be consistent with one another. As a result, credentials for one resource will be shared improperly with a different resource. The root of this weakness is that while one operation on Request (send) is in the midst of executing, a second operation may be invoked (setup).

See Also

Comprehensive Categorization: Insufficient Control Flow Management

Weaknesses in this category are related to insufficient control flow management.

State Issues

Weaknesses in this category are related to improper management of system state.

Comprehensive CWE Dictionary

This view (slice) covers all the elements in CWE.

Weakness Base Elements

This view (slice) displays only weakness base elements.


Common Weakness Enumeration content on this website is copyright of The MITRE Corporation unless otherwise specified. Use of the Common Weakness Enumeration and the associated references on this website are subject to the Terms of Use as specified by The MITRE Corporation.