You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 39 Next »

Because pointer arithmetic does not take account of polymorphism, a major problem with arrays is that they do not interact well with polymorphism (see Stroustrup 06, Meyers 06), as the following example illustrates:

Non-Compliant Code Example

class Base {
public:
	virtual void func(void) {
		cout << "Base" << endl;
	}
};

class Derived : public Base {
public:
	int i;
	Derived() { i = 0; }

	void func(void) {
		cout << "Derived " << ++i << endl;
	}
};

void walk(class Base *bar, int count) {
	for (int i = 0; i < count; i++) {
		bar[i].func();
	}
}

int main(void) {
	Base dis[3];
	Derived dat[3];

	walk(dis, 3);
	walk(dat, 3); // crashes
}

In the last call to walk(), dat[] is treated as a Base[] and the subscripting no longer works correctly when sizeof(Derived) != sizeof(Base). This is because walk() incorrectly believes that the size of each element in bar[] is sizeof(Base). To locate the second element in the array (located at bar[1]), walk() adds the sizeof(Base) to the address bar. Assuming the derived object is larger (which is often the case), the resulting pointer refers to a point within the first element and not to the start of the second element located at bar + sizeof(Derived).

Compliant Solution 1

Instead of having an array of objects, an array of pointers solves the problem of the objects being of different sizes. With the Base and Derived classes as in the Non-Compliant Code Example, we can define the walk and main methods as follows.

void walk(class Base *bar [], int count) {
	for (int i = 0; i < count; i++) {
		(bar[i])->func();
	}
}

int main(void) {
	Base* dis[3] = {new Base, new Base, new Base};
	Base* dat[3] = {new Derived, new Derived, new Derived};

	walk(dis, 3);
	walk(dat, 3);

  for (int i = 0; i < 3; i++) {
    delete dis[i];
    delete dat[i];
  }
}

The elements in the arrays are now all the same size (because they are pointers to Base or Derived objects) and there is no problem with the array indexing.

Compliant Solution 2

A better approach would be to use vectors and iterators, instead of arrays, as follows. (Note, however, that we have to have vectors of pointers because containers must be homogeneous.)

void walk(vector<Base*>bar) {
	for_each (bar.begin(), bar.end(), mem_fun(&Base::func));
}

int main(void) {
	vector<Base*> dis(3);
        for (int i=0; i<3; i++) dis[i] = new Base;

	vector<Base*> dat(3);
        for (int i=0; i<3; i++) dat[i] = new Derived;

	walk(dis);
	walk(dat);

  for (int i = 0; i < 3; i++) {
    delete dis[i];
    delete dat[i];
  }
}

Risk Assessment

Using arrays polymorphically can result in memory corruption, which could lead to an attacker being able to execute arbitrary code.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

ARR39-CPP

high

likely

high

P9

L2

Bibliography

[Sutter 04] Item 100: Don't treat arrays polymorphically.

[Meyers 96] Item 3: Never treat arrays polymorphically.

[Lockheed Martin 05] AV Rule 96 Arrays shall not be treated polymorphically.

[Stroustrup 06] What's wrong with arrays?


ARR37-CPP. Do not add or subtract an integer to a pointer to a non-array object      06. Arrays and the STL (ARR)      ARR40-CPP. Use a valid ordering rule

  • No labels