<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/58485>58485</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Static analyzer reports NULL pointer dereference when writing to memory returned from a function marked returns_nonnull
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
ryao
</td>
</tr>
</table>
<pre>
Here is a minimal test case:
```
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or https://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
#include <stdlib.h>
#include <stdio.h>
#define UMEM_NOFAIL 0x0100 /* Never fails */
#define KM_NOSLEEP 1
__attribute__((alloc_size(1), malloc))
static inline void *
umem_alloc(size_t size, int flags)
{
void *ptr = NULL;
do {
ptr = malloc(size);
} while (ptr == NULL && (flags & UMEM_NOFAIL));
return (ptr);
}
__attribute__((returns_nonnull, alloc_size(1), malloc))
static inline void *
umem_alloc_nofail(size_t size, int kmflags)
{
return (umem_alloc(size, kmflags));
}
#define kmem_alloc(_s, _f) ((((_f) & KM_NOSLEEP) == 0) ? \
umem_alloc_nofail(_s, _f) : umem_alloc(_s, _f))
struct s {
int a;
};
int example(struct s *buf, struct s **buf2) {
buf = *buf2 = kmem_alloc(4, 0);
buf->a = 0;
free(buf);
return (0);
}
```
Running Clang's Static Analyzer on it normally, with Z3 refution and with Z3 constraint checking all report a NULL pointer dereference:
```
richard@vserver ~ $ clang --analyze test-safe-malloc.c
test-safe-malloc.c:56:9: warning: Access to field 'a' results in a dereference of a null pointer (loaded from variable 'buf') [core.NullDereference]
buf->a = 0;
~~~ ^
1 warning generated.
richard@vserver ~ $ clang --analyze -Xclang -analyzer-config -Xclang crosscheck-with-z3=true test-safe-malloc.c
test-safe-malloc.c:56:9: warning: Access to field 'a' results in a dereference of a null pointer (loaded from variable 'buf') [core.NullDereference]
buf->a = 0;
~~~ ^
1 warning generated.
richard@vserver ~ $ clang --analyze -Xclang -analyzer-constraints=z3 test-safe-malloc.c
test-safe-malloc.c:56:9: warning: Access to field 'a' results in a dereference of a null pointer (loaded from variable 'buf') [core.NullDereference]
buf->a = 0;
~~~ ^
1 warning generated.
```
However, if I change the code to avoid repeated assignment, the issue goes away. In specific, it is happy with this:
```
int example(struct s *buf, struct s **buf2) {
*buf2 = kmem_alloc(4, 0);
buf = *buf2;
buf->a = 0;
free(buf);
return (0);
}
```
I realize that the way that the allocation functions in the minimal test case are a little strange. They are a result of a couple of factors:
* The codebase where the issue was found was designed to be built in both kernel space and user space, so the kernel memory allocator is replaced with a user memory allocator via the CPP.
* I needed a way to teach Clang's static analyzer when the allocation functions do not return NULL to avoid false positives from NULL being returned. This lead me to make `umem_alloc_nofail()` in a local experimental branch. This worked, with 1 exception, where the code was `buf = *ret = kmem_cache_alloc(buf_cache, KM_PUSHPAGE);`.
To make the test case "small", I wrote an example using the simpler `kmem_alloc()` function, which thankfully, also exhibited the same problem. I probably could cut off a few lines to make it even simpler, but a few attempts at this made the false positive disappear, so I Ieft it as is.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJztWFtz4rgS_jXwooIy5pLkgQcGmB1qs5nUZKbO5YWS7TZoY1uUZIdhfv35WrLBBDKps7VbtQ9LOY6sS6vV_fVNkU4O009kSCgrpMhVoXKZiZJsKWJpqTOcdYJFJ2jek6B-_Gf4sRPWQwItMV8s7sWn5Wyx_CKevs6-fD2NtWZ93ZKIdVFSUVqhU1FusXmqMhISjNgq-p3iUpQaAwRWTF7PovZOOs91IRb0Qpne5SAlZJGIhbKlUVFVKgzeq5gKS5h_y5Q6YVj3oNUJ7_otcv_RlcjlQRS6FBWWnFii7zHtSqEKsJzvMiWLmMRelVvHXU2wf_WcTDSWhdBRKbFegsLuUB9FZDVzkjc0kKQ1Md6fH5cPT5_vZ19WT_371RwfyxZFbcS2LHeW1cKy_6h3IKIrE1Nfmw06arIWTVZGb9AP2ud8ImrzLVJQ5G-7o1ilKhaZLDaV3JDY6BcyhSo2YgcNKGshUduixNLOVK5KybK2oioSMu_L5F9bKkRy1BLIz3kjSvA_gWLmkHScVUmtgjagIEKS8bZFzGMGjJzWtI7mBt-Vbls6q1TIHXQcyyhzvMgkcTRTnWV6z9xGgNv-gjee22DijD_KEisI3GmLI0YHERkZPxNwzxgcf-iMF2gIQ7tMxpjhiBygUKH3hVAJcK3SAzZuUVUFtJY7sQMH4lEbr4E50GXUZlsKED7gB-LcLGROjLr4OA7aZHjraxpqi3z50J4DxLV9QThsxN4Zzm2ZZCrqbzvD5RvDSrdHmzkJpaog8e235W_rh88fZ6t70f4F34NBEAjhPY14gLkbkUqV2XOOjoR-ZTJP98vl4xkdMWjvu17L0gOQ1mt4BzwSCo7XVv2A3m8H8A6s0tz1uo87v9Iy3GOoIOPNXrRKTtKrcsrX9YpbprQuhSfIoC5FmsmNPVLq3HyoRVv_GmK70kBiC_Hw7f6-M_zQ5ruZmmhxsbz5NcvzNie86_DVgs7NQuy3bCSYVK9q9kXXBA-POK75u62jWiTXuTNUVqaoqZ5Nu1m8owW_1K4LXRRVljkT_DMVA8IMnuv6ec7f0dDpYBeqZhqt9W8f-oTU5zaNtWUK6xQra-04efjHd7MKTuh2PV5hgW9_bIGgM55fouOaGNr7sjOp3mDqJJRa2qZCiLaXMGRByrPDN20eoe8SMZQ1eaIQzqIq5Y3aXb43dGy93gIDDuH1HNc-E-aIqQXXMI_5PfggKbzcruM3NcQcOq4uSZxAEFxX86sUyb2_VIULpXMOr53wxoonj9dZIbPDD_g0pCuqRPphGNqHYzz57xAbpj6d4UDXdCJ9grgkyzTeUvzMxLGQAwnCAVINZ8U7jQkgjshMKf6Quvw8oTMq3kqTdEbBiyXDvrZzs8RZRyJmzkWvJz3DLkPsWZlSz9tiP_YUrvQPZ-MJXneMr710OQU3Z3FM1nKa58IkdrmR-MMRbJUhQLqMqcU5hzAp2C0czwUlZFomiJup0bl4kUZx5GZSTns3Dj_jD7E21H_AykVLEMfw93NsnNzl0j_evuo4NmhOJDaEmCpLSvr_ryh7_6576g7Tg3ZTtTkOxEZb69TcY_33fgzBIozlHy381VqobQz59uLH8B9pvyftq77vk95z1uaibCpW8FcQs0_VY805uxbSxWv4LmJiQqLY2BRc0_Einojyo-KShFCk7uWhL1bFsWRxhEuuX7dI3g9NHq7sz13dnxaO_lgYakewv02YWmGZzBS79y0qJ5Y8pH36cCdzhYdIqyL2ZQcwy2MXVweumJeoEcsS8GRLgtr7XPwf6iGPeo_xWFfQBLdTGZfaXGjveG-QUMTU91u-tTiBYy9RsOuKYyRaCTGEgCWgKyIIVGWuho80wPGMwpYyAAgll4uqqPiN_3SK9xcP9SxoVJtDc3SUywDaebkm_fqLiS9KOkLzx8f-6RArURCx-UovW2zGVW0rNahT2cYP8UmLt8WPWoAvLWp9u7B_tKhUZpDUTltVqhfYjvMYbkpEbL9-EcwXosWxMpIJjsHrc_kMlzIJruWMjKdJ4H0VM5TBjnZkFBssPlDhFvG2JrnX5pmSYzozqK9TuHTlvqMOnSdgxYFyyzrA4MmuYoiJjtaFWb6HCSEvfvz29Olx9suyRvukufbw76_1kfyNUoNQlN6WXba7D5pDNXujS4ZE4xmgWZaTuyFR3GGYwTMrr6XRKMQfC1GHraZ4Tqs6m4MmNKhuVaTYxTmKXJfvjIb3zuHRXBOu_MC2gNgQV2wabBsp7QWXNfaoGfg7-NSiYYo3QBlVT0VVRfkO0cRZLZSQy_pq5BwPfA0Dj0nS1KhfiRWlJROHIpTtd2k6mEwm4WAS3Iy6yXSY3A3vZLdUMOjp0yuU-szTvpl4ehjvjXKXPnwQby8NBj045VGQ4JqhI16VhN3KZNPzO7ANkFVF_VjnfP-VvTT_ehAo3yTi07kIvhMb345ux93tdCCTWzmU42EkZUhRGowlje7kbRBNRqNwcNfNZESZnfIVTRgWkKoj4S5tFl01DYMwHASDu3AwuhmP-kGYJJIGQxomSZzcpcg3KIe19JkPvpnrmqljKapQII6CTNnSngZ9wCOa-huhrqzKrTZTc5C66_adOr7_Bya8oE8">