On Fri, Sep 13, 2024 at 12:33:00PM +0200, Jakub Jelinek wrote:
> Hi!
> 
> Jonathan reported on IRC that certain unnamed proprietary static analyzer
> is unhappy about the new finish_embed function and it is actually right.
> On a testcase like:
> #embed __FILE__ limit (0) if_empty (0)
> params->if_empty.count is 1, limit is 0, so count is 0 (we need just
> a single token and one fits into pfile->directive_result).  Because
> count is 0, we don't allocate toks, so it stays NULL, and then in
> 1301    if (prefix->count)
> 1302      {
> 1303        *tok = *prefix->base_run.base;
> 1304        tok = toks;
> 1305        tokenrun *cur_run = &prefix->base_run;
> 1306        while (cur_run)
> 1307          {
> 1308            size_t cnt = (cur_run->next ? cur_run->limit
> 1309                          : prefix->cur_token) - cur_run->base;
> 1310            cpp_token *t = cur_run->base;
> 1311            if (cur_run == &prefix->base_run)
> 1312              {
> 1313                t++;
> 1314                cnt--;
> 1315              }
> 1316            memcpy (tok, t, cnt * sizeof (cpp_token));
> 1317            tok += cnt;
> 1318            cur_run = cur_run->next;
> 1319          }
> 1320      }
> the *tok = *prefix->base_run.base; assignment will copy the only
> token.  cur_run is still non-NULL, cnt will be initially 1 and
> then decremented to 0, but we invoke UB because we do
> memcpy (NULL, cur_run->base + 1, 0 * sizeof (cpp_token));
> and then the loop stops because cur_run->next must be NULL.
> 
> As we don't really copy anything, toks can be anything non-NULL,
> so the following patch fixes that by initializing toks also to
> &pfile->directive_result (just something known to be non-NULL).
> This should be harmless even for the
> #embed __FILE__ limit (1)
> case (no non-empty prefix/suffix) where toks isn't allocated
> either, but in that case prefix->count will be 0 and in the
> 1321    for (size_t i = 0; i < limit; ++i)
> 1322      {
> 1323        tok->src_loc = params->loc;
> 1324        tok->type = CPP_NUMBER;
> 1325        tok->flags = NO_EXPAND;
> 1326        if (i == 0)
> 1327          tok->flags |= PREV_WHITE;
> 1328        tok->val.str.text = s;
> 1329        tok->val.str.len = sprintf ((char *) s, "%d", buffer[i]);
> 1330        s += tok->val.str.len + 1;
> 1331        if (tok == &pfile->directive_result)
> 1332          tok = toks;
> 1333        else
> 1334          tok++;
> 1335        if (i < limit - 1)
> 1336          {
> 1337            tok->src_loc = params->loc;
> 1338            tok->type = CPP_COMMA;
> 1339            tok->flags = NO_EXPAND;
> 1340            tok++;
> 1341          }
> 1342      }
> loop limit will be 1, so tok is initially &pfile->directive_result,
> that is stilled in, then tok = toks; (previously setting tok to NULL,
> now to &pfile->directive_result again) and because 0 < 1 - 1 is
> false, nothing further will happen and the loop will finish (and as
> params->suffix.count will be 0, nothing further will use tok).
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.
 
> 2024-09-12  Jakub Jelinek  <ja...@redhat.com>
> 
>       * files.cc (finish_embed): Initialize toks to tok rather
>       than NULL.
> 
> --- libcpp/files.cc.jj        2024-09-12 18:16:49.995409074 +0200
> +++ libcpp/files.cc   2024-09-12 22:55:01.619765364 +0200
> @@ -1284,7 +1284,7 @@ finish_embed (cpp_reader *pfile, _cpp_fi
>      }
>    uchar *s = len ? _cpp_unaligned_alloc (pfile, len) : NULL;
>    _cpp_buff *tok_buff = NULL;
> -  cpp_token *toks = NULL, *tok = &pfile->directive_result;
> +  cpp_token *tok = &pfile->directive_result, *toks = tok;
>    size_t count = 0;
>    if (limit)
>      count = (params->prefix.count + limit * 2 - 1
> 
>       Jakub
> 

Marek

Reply via email to