[CDRIVER-2129] crash with bson_iter_find_descendant and array of documents Created: 18/Apr/17  Updated: 27/Oct/23  Resolved: 25/Apr/17

Status: Closed
Project: C Driver
Component/s: libbson
Affects Version/s: 1.6.2
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Olivier Bertrand [X] Assignee: Hannes Magnusson
Resolution: Works as Designed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Windows 10 64bits



 Description   

Working on the following collection:

{"_id":{"$oid":"58e17a4dd7b07d1fee077fac"},"item":"journal","instock":[{"warehouse":"A","qty":5.0},{"warehouse":"C","qty":15.0}]}
{"_id":{"$oid":"58e17a4dd7b07d1fee077fad"},"item":"notebook","instock":[{"warehouse":"C","qty":5.0}]}
{"_id":{"$oid":"58e17a4dd7b07d1fee077fae"},"item":"paper","instock":[{"warehouse":"A","qty":60.0},{"warehouse":"B","qty":15.0}]}
{"_id":{"$oid":"58e17a4dd7b07d1fee077faf"},"item":"planner","instock":[{"warehouse":"A","qty":40.0},{"warehouse":"B","qty":5.0}]}
{"_id":{"$oid":"58e17a4dd7b07d1fee077fb0"},"item":"postcard","instock":[{"warehouse":"B","qty":15.0},{"warehouse":"C","qty":35.0}]}

When finding the "instock" item by:

bson_iter_find_descendant(&Iter, "instock", &Desc))

it is correctly of type ARRAY and can be printed with:

if (BSON_ITER_HOLDS_ARRAY(&Iter)) {
  char *str = NULL;
  bson_t *arr;
  const uint8_t *data = NULL;
  uint32_t len = 0;
 
  bson_iter_array(&Desc, &len, &data);
  arr = bson_new_from_data(data, len);
  str = bson_as_json(arr, NULL);
  bson_free(str);
  bson_destroy(arr);

str is:

{"0":{"warehouse":"A","qty":5.0},"1":{"warehouse":"C","qty":15.0}}
{"0":{"warehouse":"C","qty":5.0}}
{"0":{"warehouse":"A","qty":60.0},"1":{"warehouse":"B","qty":15.0}}
{"0":{"warehouse":"A","qty":40.0},"1":{"warehouse":"B","qty":5.0}}
{"0":{"warehouse":"B","qty":15.0},"1":{"warehouse":"C","qty":35.0}}

It is also possible to retrieve the values of wareahouse or qty by something like:

bson_iter_find_descendant(&Iter, "instock.0.warehouse", &Desc))
or
bson_iter_find_descendant(&Iter, "instock.1.qty", &Desc))

However when using:

bson_iter_find_descendant(&Iter, "instock.0", &Desc))

One would expect to retrieve an item of type DOCUMENT. But instead it is retrieved as type ARRAY and when trying to execute:

bson_iter_document(&Desc, &len, &data);
arr = bson_new_from_data(data, len);

data is null and the program crashes.



 Comments   
Comment by Hannes Magnusson [ 25/Apr/17 ]

I think you are re-using the same iterator, and bson_iter_find_descendant is returning false, without updating the re-used Desc subiterator, which is still pointing to the results for the first bson_iter_find_descendant which was just instock.

bson_iter_find_descendant(&Iter, "instock", &Desc); // Will position the iterator inside "instock", representing the array
bson_iter_find_descendant(&Iter, "instock.0", &Desc); // the iterator has already seeked into "instock", and there is no remaining matching element
// Desc hasn't been changed, and is still pointing at the "instock" array

If you however create another iterator, you'll will be able to get to instock.0 as a document.

Here is an example that doesn't re-use the same iterator and works as expected:

#include <mongoc.h>
 
int
main ()
{
   bson_error_t error;
   const char *sdoc = "{\"item\":\"journal\",\"instock\": [{\"warehouse\":\"A\",\"qty\":5.0},{\"warehouse\":\"C\",\"qty\":15.0}]}";
   char *res;
   bson_t *doc;
   bson_iter_t iter;
   bson_iter_t iter2;
   bson_iter_t desc;
   doc = bson_new_from_json ((const uint8_t *) sdoc, strlen (sdoc), &error);
 
   res = bson_as_json (doc, 0);
   fprintf (stderr, "JSON: %s\n", res);
 
 
   bson_iter_init (&iter, doc);
   BSON_ASSERT (bson_iter_find_descendant (&iter, "instock", &desc));
   // The iterator has now iterated passed "item", and is now pointing
   // at the first element in the "stock" array.
   BSON_ASSERT (BSON_ITER_HOLDS_ARRAY (&desc));
   if (BSON_ITER_HOLDS_ARRAY (&desc)) {
      char *str = NULL;
      bson_t *arr;
      const uint8_t *data = NULL;
      uint32_t len = 0;
 
      bson_iter_array (&desc, &len, &data);
      arr = bson_new_from_data (data, len);
      str = bson_as_json (arr, NULL);
      fprintf (stderr, "instock JSON: %s\n", str);
 
      bson_free (str);
      bson_destroy (arr);
   }
 
 
   // Create another iterator pointing at the start of the BSON
   bson_iter_init (&iter2, doc);
   BSON_ASSERT (bson_iter_find_descendant (&iter2, "instock.0", &desc));
   BSON_ASSERT (BSON_ITER_HOLDS_DOCUMENT (&desc));
   if (BSON_ITER_HOLDS_DOCUMENT (&desc)) {
      char *str = NULL;
      bson_t *arr;
      const uint8_t *data = NULL;
      uint32_t len = 0;
 
      bson_iter_document (&desc, &len, &data);
      arr = bson_new_from_data (data, len);
      str = bson_as_json (arr, NULL);
      fprintf (stderr, "instock.0 (document): %s\n", str);
 
      bson_free (str);
      bson_destroy (arr);
   }
 
 
   bson_free (res);
   bson_destroy (doc);
 
   return 0;
}

Comment by Olivier Bertrand [X] [ 18/Apr/17 ]

(Sorry I mistyped the last command that is "bson_iter_array" not "bson_iter_document")

However, what is interresting is that if, when data is null, I try "bson_iter_document" it works and I can print the subdocument. This prooves that the error is just in returning the wrong type but the item is correctly retrieved.

Generated at Wed Feb 07 21:14:16 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.