<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">