mirror of
https://github.com/nlohmann/json.git
synced 2026-10-01 13:34:14 +07:00
Document that update() and merge_patch() must not alias *this
update() and merge_patch() read their argument while they modify *this. When the argument is *this or a value nested inside *this (for example a subobject returned by operator[]), the modification destroys or relocates the value while it is still being iterated, so the functions read freed memory or dereference invalidated iterators (heap-use-after-free, or an uncaught invalid_iterator.214 for update()). This reproduces with plain std::map-backed json and, for update() on ordered_json, also via reallocation of the underlying vector. A fix would require copying the argument whenever it may alias *this, which cannot be checked in constant time without parent pointers (only available under JSON_DIAGNOSTICS), and would cost an unconditional deep copy per call otherwise. The maintainer decided to document the restriction instead of changing the library. Add a "Notes" section with a "!!! danger" admonition to update.md and merge_patch.md explaining that the argument must not be *this or refer into *this, and showing the workaround of passing a copy, e.g. j.update(json(j["a"])) and j.merge_patch(json(j)). Fixes #5641. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -37,6 +37,22 @@ Thereby, `Target` is the current object; that is, the patch is applied to the cu
|
||||
|
||||
Linear in the lengths of `apply_patch`.
|
||||
|
||||
## Notes
|
||||
|
||||
!!! danger "Undefined behavior"
|
||||
|
||||
`merge_patch()` reads `apply_patch` while it modifies `#!cpp *this`. `apply_patch` must not be `#!cpp *this`
|
||||
itself and must not refer to a value contained in `#!cpp *this` (for example, a subobject returned by
|
||||
`#!cpp (*this)[key]`). Calling `merge_patch()` with such an argument reads the argument after it has been
|
||||
invalidated by the modification, which is undefined behavior. If the patch may alias `#!cpp *this`, pass a copy
|
||||
instead:
|
||||
|
||||
```cpp
|
||||
j.merge_patch(json(j)); // instead of j.merge_patch(j)
|
||||
```
|
||||
|
||||
See [GitHub issue #5641](https://github.com/nlohmann/json/issues/5641) for more information.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
@@ -61,3 +77,5 @@ Linear in the lengths of `apply_patch`.
|
||||
## Version history
|
||||
|
||||
- Added in version 3.0.0.
|
||||
- Documented that `apply_patch` must not be `#!cpp *this` or refer to a value contained in `#!cpp *this`, in version
|
||||
3.13.0.
|
||||
|
||||
@@ -59,6 +59,22 @@ Basic guarantee: if an exception is thrown during the operation, the JSON value
|
||||
1. O(N*log(size() + N)), where N is the number of elements to insert.
|
||||
2. O(N*log(size() + N)), where N is the number of elements to insert.
|
||||
|
||||
## Notes
|
||||
|
||||
!!! danger "Undefined behavior"
|
||||
|
||||
Both overloads read the argument while they modify `#!cpp *this`. The argument `j` (or, for overload (2), the
|
||||
range `[first, last)`) must not be `#!cpp *this` itself and must not refer to a value contained in
|
||||
`#!cpp *this` (for example, a subobject returned by `#!cpp (*this)[key]`). Calling `update()` with such an
|
||||
argument reads the argument after it has been invalidated by the modification, which is undefined behavior. If
|
||||
the argument may alias `#!cpp *this`, pass a copy instead:
|
||||
|
||||
```cpp
|
||||
j.update(json(j["defaults"])); // instead of j.update(j["defaults"])
|
||||
```
|
||||
|
||||
See [GitHub issue #5641](https://github.com/nlohmann/json/issues/5641) for more information.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
@@ -155,3 +171,5 @@ Basic guarantee: if an exception is thrown during the operation, the JSON value
|
||||
|
||||
- Added in version 3.0.0.
|
||||
- Added `merge_objects` parameter in 3.10.5.
|
||||
- Documented that the argument must not be `#!cpp *this` or refer to a value contained in `#!cpp *this`, in version
|
||||
3.13.0.
|
||||
|
||||
Reference in New Issue
Block a user