Bug: C-C TB uninitialized memory access during xpcshell-test, comm/mailnews/base/test/unit/test_postPluginFilters.js, Uninitialised value was created by a stack allocation     at ... nsMsgDatabase::SetStringPropertyByHdr(nsIMsgDBHdr*, char const*, char const*)
Status: RESOLVED
Priority: --
Resolution: FIXED
Created: 2020-01-25 04:45:35+00:00
Resolved: 2021-01-06 17:08:00+00:00

Here is yet another one of the uninitialized memory accesses during a run of C-C TB
xpcshell-test under valgrind.

I have a feeling that this problem will result in
incorrect processing of filters, but I am not sure. Anything goes actually.

I am attaching a log from serialized verbose run of xpcshell-test.
The line numbers in the log may be slightly off due to local changes.

It contains a lot of local debug dump, but you can discern the valgrind output.
(I am running xpcshell test by saving xpcshell binary into a file named |xpcshell-bin| and then create a binary named |xpcshell| that actually runs |xpcshell-bin| under valgrind.
Lines such as the following are from this wrapper:
```
73:41.81 pid:462226 sizeof(prepargs)=120
73:41.81 pid:462226 argc=31
73:41.82 pid:462226 finalargs[  0] = /usr/local/bin/valgrind
73:41.82 pid:462226 finalargs[  1] = --track-origins=yes
73:41.82 pid:462226 finalargs[  2] = --trace-children=yes
...
```

The test that triggers the uninitialized errors is
comm/mailnews/base/test/unit/test_postPluginFilters.js

All uninitializsed value(s) were created in the following function.

```
Uninitialised value was created by a stack allocation
    at 0x59E22AA: nsMsgDatabase::SetStringPropertyByHdr(nsIMsgDBHdr*, char const*, char const*) (nsMsgDatabase.cpp:2164)
```

The first use of the value seems to be:
```
72:48.56 pid:546339 ==546339== Conditional jump or move depends on uninitialised value(s)
72:48.56 pid:546339 ==546339==	  at 0x6D66962: XPCConvert::NativeData2JS(JSContext*, JS::MutableHandle<JS::Value>, void const*, nsXPTType const&, nsID const*, unsigned int, nsresult*) (Value.h:996)
--
```

The function in Value.h:996 is:
```
static constexpr Value NumberValue(uint32_t i) {
  return i <= JSVAL_INT_MAX ? Int32Value(int32_t(i))
			    : Value::fromDouble(double(i)); <--- line 996
}
```
So this means an integer value (in C++) is being converted to a value in JS world.

Looking at the stack, it seems that the call sequence was triggered by some type of
function that is invoked at "On..."?

Anyway, what (unsigned) integer value do we create in SetStringPropertyByHdr() that are not initialized?

The function in full.


```
NS_IMETHODIMP nsMsgDatabase::SetStringPropertyByHdr(nsIMsgDBHdr *msgHdr,
						    const char *aProperty,
						    const char *aValue) {
  // don't do notifications if message not yet added to database.
  // Ignore errors (consequences of failure are minor).
  bool notify = true;
  nsMsgKey key = nsMsgKey_None;
  msgHdr->GetMessageKey(&key);
  ContainsKey(key, &notify);

  nsCString oldValue;
  nsresult rv = msgHdr->GetStringProperty(aProperty, getter_Copies(oldValue));
  NS_ENSURE_SUCCESS(rv, rv);

  // if no change to this string property, bail out
  if (oldValue.Equals(aValue)) return NS_OK;

  // Precall OnHdrPropertyChanged to store prechange status
  nsTArray<uint32_t> statusArray(m_ChangeListeners.Length());
  uint32_t status;
  nsCOMPtr<nsIDBChangeListener> listener;
  if (notify) {
    nsTObserverArray<nsCOMPtr<nsIDBChangeListener> >::ForwardIterator listeners(
	m_ChangeListeners);
    while (listeners.HasMore()) {
      listener = listeners.GetNext();
      listener->OnHdrPropertyChanged(msgHdr, true, &status, nullptr);
      // ignore errors, but append element to keep arrays in sync
      statusArray.AppendElement(status);
    }
  }

  rv = msgHdr->SetStringProperty(aProperty, aValue);
  NS_ENSURE_SUCCESS(rv, rv);

  // Postcall OnHdrPropertyChanged to process the change
  if (notify) {
    // if this is the junk score property notify, as long as we're not going
    // from no value to non junk
    if (!strcmp(aProperty, "junkscore") &&
	!(oldValue.IsEmpty() && !strcmp(aValue, "0")))
      NotifyJunkScoreChanged(nullptr);

    nsTObserverArray<nsCOMPtr<nsIDBChangeListener> >::ForwardIterator listeners(
	m_ChangeListeners);
    for (uint32_t i = 0; listeners.HasMore(); i++) {
      listener = listeners.GetNext();
      status = statusArray[i];
      listener->OnHdrPropertyChanged(msgHdr, false, &status, nullptr);
      // ignore errors
    }
  }

  return NS_OK;
}
```
I checked   |nsCString oldValue| is innocent. Even if I initialize it by the following, the
uninitialized access persists. Besides such usage of nsCString permeats C-C code and they all work. So this particular declaration should not be an exception.
```
  nsCString oldValue("");
```
Looking at the code I notice two unsigned scalar values that are not initialized. (Access to an array ELEMENT is unsigned scalar value, so I count the array as part of investigation.)
```
nsTArray<uint32_t> statusArray(m_ChangeListeners.Length());
unit32_t status;
```
|status| ought to be set in the first call to
```
      listener->OnHdrPropertyChanged(msgHdr, true, &status, nullptr);
```
and is used on the second call to
```
      status = statusArray[i];
      listener->OnHdrPropertyChanged(msgHdr, false, &status, nullptr);
```

So |statusArray[i]| used in the second while-loop  is uninitialized?
But |statusArray| is populated in the first call.

Aha, I think I found the culprit.
The following declaration seems to be incorrect.
```
  nsTArray<uint32_t> statusArray(m_ChangeListeners.Length());
```

This should be
```
  nsTArray<uint32_t> statusArray(0);
```
because the value of returned |status| is APPENDED to the array by
```
      statusArray.AppendElement(status);
```

So I take that the first |m_ChangeListeners.Length()| elements of |statusArray| stay uninitialized in the current code, and then in the second loop is referenced by 
```
   for (uint32_t i = 0; listeners.HasMore(); i++) {
      listener = listeners.GetNext();
      status = statusArray[i];   <--- Here! From 0..listeners.Length() - 1, they are uninitialised?
      listener->OnHdrPropertyChanged(msgHdr, false, &status, nullptr);
```


I thought we can modify the first loop that runs through the listeners and sets value to statusArray to use |statusArray[i++] = status| or some such.
But given the style of using |.Next()| to loop through objects, I think simply creating an array object of length 0 is better.

What do people think of the analysis above?

CAVEAT EMPTOR: there seems to be an implicit assumption that the number of listeners DO NOT CHANGE AT ALL through this code, meaning TB's execution after this function is first called. Is this true and dependable?

BTW, I am runnintg the xpcshell-test under valgrind for another bug/fix.
It takes about 13 hours on my PC and would not want to terminate it to rebuild TB (and replace the currently running binary) since it is into about fifth hour of execution now.
(Actually, not exactly the WHOLE xpcshell-test. Some tests time out due to slowdown caused by valgrind . Although I tried to lengthen the timeout, I still cannot run some tests yet.)

I will test the proposed change of declaring the array of 0-length tomorrow. Since this uninitialized memory access happens only in one test, the valgrind verification would not take that long.

In the meantime, if someone finds the different cause (i.e., my analysis is wrong), please let me know.

TIA