Merge pull request from GHSA-48w2-rm65-62xx

* Fix HTTP request smuggling vulnerability

See GHSA-48w2-rm65-62xx or CVE-2021-41136 for more info.

* 4.3.9 release note

* 5.5.1 release note

* 5.5.1

int puma_parser_init(puma_parser *parser)  {
  int cs = 0;

#line 53 "ext/puma_http11/http11_parser.c"
	{
	cs = puma_parser_start;
	}

#line 89 "ext/puma_http11/http11_parser.rl"
  parser->cs = cs;
  parser->body_start = 0;
  parser->content_len = 0;
  parser->mark = 0;
  parser->nread = 0;
  parser->field_len = 0;
  parser->field_start = 0;
  parser->request = Qnil;
  parser->body = Qnil;

  return 1;
}

Puma is a HTTP 1.1 server for Ruby/Rack applications. Prior to versions 5.5.1 and 4.3.9, using `puma` with a proxy which forwards HTTP header values which contain the LF character could allow HTTP request smugggling. A client could smuggle a request through a proxy, causing the proxy to send a response back to another unknown client. The only proxy which has this behavior, as far as the Puma team is aware of, is Apache Traffic Server. If the proxy uses persistent connections and the client adds another request in via HTTP pipelining, the proxy may mistake it as the first request's body. Puma, however, would see it as two requests, and when processing the second request, send back a response that the proxy does not expect. If the proxy has reused the persistent connection to Puma to send another request for a different client, the second response from the first client will be sent to the second client. This vulnerability was patched in Puma 5.5.1 and 4.3.9. As a workaround, do not use Apache Traffic Server with `puma`.


int puma_parser_has_error(puma_parser *parser) {
  return parser->cs == puma_parser_error;
}


int puma_parser_is_finished(puma_parser *parser) {
  return parser->cs >= puma_parser_first_final;
}


int puma_parser_finish(puma_parser *parser)
{
  if (puma_parser_has_error(parser) ) {
    return -1;
  } else if (puma_parser_is_finished(parser) ) {
    return 1;
  } else {
    return 0;
  }
}


size_t puma_parser_execute(puma_parser *parser, const char *buffer, size_t len, size_t off)  {
  const char *p, *pe;
  int cs = parser->cs;

  assert(off <= len && "offset past end of buffer");

  p = buffer+off;
  pe = buffer+len;

  /* assert(*pe == '\0' && "pointer does not end on NUL"); */
  assert((size_t) (pe - p) == len - off && "pointers aren't same distance");

#line 87 "ext/puma_http11/http11_parser.c"
	{
	if ( p == pe )
		goto _test_eof;
	switch ( cs )
	{
case 1:
	switch( (*p) ) {
		case 36: goto tr0;
		case 95: goto tr0;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto tr0;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto tr0;
	} else
		goto tr0;
	goto st0;
st0:
cs = 0;
	goto _out;
tr0:
#line 37 "ext/puma_http11/http11_parser.rl"
	{ MARK(mark, p); }
	goto st2;
st2:
	if ( ++p == pe )
		goto _test_eof2;
case 2:
#line 118 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st27;
		case 95: goto st27;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st27;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st27;
	} else
		goto st27;
	goto st0;
tr2:
#line 50 "ext/puma_http11/http11_parser.rl"
	{
    parser->request_method(parser, PTR_TO(mark), LEN(mark, p));
  }
	goto st3;
st3:
	if ( ++p == pe )
		goto _test_eof3;
case 3:
#line 143 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 42: goto tr4;
		case 43: goto tr5;
		case 47: goto tr6;
		case 58: goto tr7;
	}
	if ( (*p) < 65 ) {
		if ( 45 <= (*p) && (*p) <= 57 )
			goto tr5;
	} else if ( (*p) > 90 ) {
		if ( 97 <= (*p) && (*p) <= 122 )
			goto tr5;
	} else
		goto tr5;
	goto st0;
tr4:
#line 37 "ext/puma_http11/http11_parser.rl"
	{ MARK(mark, p); }
	goto st4;
st4:
	if ( ++p == pe )
		goto _test_eof4;
case 4:
#line 167 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 32: goto tr8;
		case 35: goto tr9;
	}
	goto st0;
tr8:
#line 53 "ext/puma_http11/http11_parser.rl"
	{
    parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
  }
	goto st5;
tr31:
#line 37 "ext/puma_http11/http11_parser.rl"
	{ MARK(mark, p); }
#line 56 "ext/puma_http11/http11_parser.rl"
	{
    parser->fragment(parser, PTR_TO(mark), LEN(mark, p));
  }
	goto st5;
tr33:
#line 56 "ext/puma_http11/http11_parser.rl"
	{
    parser->fragment(parser, PTR_TO(mark), LEN(mark, p));
  }
	goto st5;
tr37:
#line 69 "ext/puma_http11/http11_parser.rl"
	{
    parser->request_path(parser, PTR_TO(mark), LEN(mark,p));
  }
#line 53 "ext/puma_http11/http11_parser.rl"
	{
    parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
  }
	goto st5;
tr41:
#line 60 "ext/puma_http11/http11_parser.rl"
	{ MARK(query_start, p); }
#line 61 "ext/puma_http11/http11_parser.rl"
	{
    parser->query_string(parser, PTR_TO(query_start), LEN(query_start, p));
  }
#line 53 "ext/puma_http11/http11_parser.rl"
	{
    parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
  }
	goto st5;
tr44:
#line 61 "ext/puma_http11/http11_parser.rl"
	{
    parser->query_string(parser, PTR_TO(query_start), LEN(query_start, p));
  }
#line 53 "ext/puma_http11/http11_parser.rl"
	{
    parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
  }
	goto st5;
st5:
	if ( ++p == pe )
		goto _test_eof5;
case 5:
#line 229 "ext/puma_http11/http11_parser.c"
	if ( (*p) == 72 )
		goto tr10;
	goto st0;
tr10:
#line 37 "ext/puma_http11/http11_parser.rl"
	{ MARK(mark, p); }
	goto st6;
st6:
	if ( ++p == pe )
		goto _test_eof6;
case 6:
#line 241 "ext/puma_http11/http11_parser.c"
	if ( (*p) == 84 )
		goto st7;
	goto st0;
st7:
	if ( ++p == pe )
		goto _test_eof7;
case 7:
	if ( (*p) == 84 )
		goto st8;
	goto st0;
st8:
	if ( ++p == pe )
		goto _test_eof8;
case 8:
	if ( (*p) == 80 )
		goto st9;
	goto st0;
st9:
	if ( ++p == pe )
		goto _test_eof9;
case 9:
	if ( (*p) == 47 )
		goto st10;
	goto st0;
st10:
	if ( ++p == pe )
		goto _test_eof10;
case 10:
	if ( 48 <= (*p) && (*p) <= 57 )
		goto st11;
	goto st0;
st11:
	if ( ++p == pe )
		goto _test_eof11;
case 11:
	if ( (*p) == 46 )
		goto st12;
	if ( 48 <= (*p) && (*p) <= 57 )
		goto st11;
	goto st0;
st12:
	if ( ++p == pe )
		goto _test_eof12;
case 12:
	if ( 48 <= (*p) && (*p) <= 57 )
		goto st13;
	goto st0;
st13:
	if ( ++p == pe )
		goto _test_eof13;
case 13:
	if ( (*p) == 13 )
		goto tr18;
	if ( 48 <= (*p) && (*p) <= 57 )
		goto st13;
	goto st0;
tr18:
#line 65 "ext/puma_http11/http11_parser.rl"
	{
    parser->http_version(parser, PTR_TO(mark), LEN(mark, p));
  }
	goto st14;
tr26:
#line 46 "ext/puma_http11/http11_parser.rl"
	{ MARK(mark, p); }
#line 47 "ext/puma_http11/http11_parser.rl"
	{
    parser->http_field(parser, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
  }
	goto st14;
tr29:
#line 47 "ext/puma_http11/http11_parser.rl"
	{
    parser->http_field(parser, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
  }
	goto st14;
st14:
	if ( ++p == pe )
		goto _test_eof14;
case 14:
#line 322 "ext/puma_http11/http11_parser.c"
	if ( (*p) == 10 )
		goto st15;
	goto st0;
st15:
	if ( ++p == pe )
		goto _test_eof15;
case 15:
	switch( (*p) ) {
		case 13: goto st16;
		case 33: goto tr21;
		case 124: goto tr21;
		case 126: goto tr21;
	}
	if ( (*p) < 45 ) {
		if ( (*p) > 39 ) {
			if ( 42 <= (*p) && (*p) <= 43 )
				goto tr21;
		} else if ( (*p) >= 35 )
			goto tr21;
	} else if ( (*p) > 46 ) {
		if ( (*p) < 65 ) {
			if ( 48 <= (*p) && (*p) <= 57 )
				goto tr21;
		} else if ( (*p) > 90 ) {
			if ( 94 <= (*p) && (*p) <= 122 )
				goto tr21;
		} else
			goto tr21;
	} else
		goto tr21;
	goto st0;
st16:
	if ( ++p == pe )
		goto _test_eof16;
case 16:
	if ( (*p) == 10 )
		goto tr22;
	goto st0;
tr22:
#line 73 "ext/puma_http11/http11_parser.rl"
	{
    parser->body_start = p - buffer + 1;
    parser->header_done(parser, p + 1, pe - p - 1);
    {p++; cs = 46; goto _out;}
  }
	goto st46;
st46:
	if ( ++p == pe )
		goto _test_eof46;
case 46:
#line 373 "ext/puma_http11/http11_parser.c"
	goto st0;
tr21:
#line 40 "ext/puma_http11/http11_parser.rl"
	{ MARK(field_start, p); }
#line 41 "ext/puma_http11/http11_parser.rl"
	{ snake_upcase_char((char *)p); }
	goto st17;
tr23:
#line 41 "ext/puma_http11/http11_parser.rl"
	{ snake_upcase_char((char *)p); }
	goto st17;
st17:
	if ( ++p == pe )
		goto _test_eof17;
case 17:
#line 389 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 33: goto tr23;
		case 58: goto tr24;
		case 124: goto tr23;
		case 126: goto tr23;
	}
	if ( (*p) < 45 ) {
		if ( (*p) > 39 ) {
			if ( 42 <= (*p) && (*p) <= 43 )
				goto tr23;
		} else if ( (*p) >= 35 )
			goto tr23;
	} else if ( (*p) > 46 ) {
		if ( (*p) < 65 ) {
			if ( 48 <= (*p) && (*p) <= 57 )
				goto tr23;
		} else if ( (*p) > 90 ) {
			if ( 94 <= (*p) && (*p) <= 122 )
				goto tr23;
		} else
			goto tr23;
	} else
		goto tr23;
	goto st0;
tr24:
#line 42 "ext/puma_http11/http11_parser.rl"
	{
    parser->field_len = LEN(field_start, p);
  }
	goto st18;
tr27:
#line 46 "ext/puma_http11/http11_parser.rl"
	{ MARK(mark, p); }
	goto st18;
st18:
	if ( ++p == pe )
		goto _test_eof18;
case 18:
#line 428 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 9: goto tr25;
		case 13: goto tr26;
		case 32: goto tr27;
	}
	if ( 33 <= (*p) && (*p) <= 126 )
		goto tr25;
	goto st0;
tr25:
#line 46 "ext/puma_http11/http11_parser.rl"
	{ MARK(mark, p); }
	goto st19;
st19:
	if ( ++p == pe )
		goto _test_eof19;
case 19:
#line 445 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 9: goto st19;
		case 13: goto tr29;
	}
	if ( 32 <= (*p) && (*p) <= 126 )
		goto st19;
	goto st0;
tr9:
#line 53 "ext/puma_http11/http11_parser.rl"
	{
    parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
  }
	goto st20;
tr38:
#line 69 "ext/puma_http11/http11_parser.rl"
	{
    parser->request_path(parser, PTR_TO(mark), LEN(mark,p));
  }
#line 53 "ext/puma_http11/http11_parser.rl"
	{
    parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
  }
	goto st20;
tr42:
#line 60 "ext/puma_http11/http11_parser.rl"
	{ MARK(query_start, p); }
#line 61 "ext/puma_http11/http11_parser.rl"
	{
    parser->query_string(parser, PTR_TO(query_start), LEN(query_start, p));
  }
#line 53 "ext/puma_http11/http11_parser.rl"
	{
    parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
  }
	goto st20;
tr45:
#line 61 "ext/puma_http11/http11_parser.rl"
	{
    parser->query_string(parser, PTR_TO(query_start), LEN(query_start, p));
  }
#line 53 "ext/puma_http11/http11_parser.rl"
	{
    parser->request_uri(parser, PTR_TO(mark), LEN(mark, p));
  }
	goto st20;
st20:
	if ( ++p == pe )
		goto _test_eof20;
case 20:
#line 495 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 32: goto tr31;
		case 60: goto st0;
		case 62: goto st0;
		case 127: goto st0;
	}
	if ( (*p) > 31 ) {
		if ( 34 <= (*p) && (*p) <= 35 )
			goto st0;
	} else if ( (*p) >= 0 )
		goto st0;
	goto tr30;
tr30:
#line 37 "ext/puma_http11/http11_parser.rl"
	{ MARK(mark, p); }
	goto st21;
st21:
	if ( ++p == pe )
		goto _test_eof21;
case 21:
#line 516 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 32: goto tr33;
		case 60: goto st0;
		case 62: goto st0;
		case 127: goto st0;
	}
	if ( (*p) > 31 ) {
		if ( 34 <= (*p) && (*p) <= 35 )
			goto st0;
	} else if ( (*p) >= 0 )
		goto st0;
	goto st21;
tr5:
#line 37 "ext/puma_http11/http11_parser.rl"
	{ MARK(mark, p); }
	goto st22;
st22:
	if ( ++p == pe )
		goto _test_eof22;
case 22:
#line 537 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 43: goto st22;
		case 58: goto st23;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st22;
	} else if ( (*p) > 57 ) {
		if ( (*p) > 90 ) {
			if ( 97 <= (*p) && (*p) <= 122 )
				goto st22;
		} else if ( (*p) >= 65 )
			goto st22;
	} else
		goto st22;
	goto st0;
tr7:
#line 37 "ext/puma_http11/http11_parser.rl"
	{ MARK(mark, p); }
	goto st23;
st23:
	if ( ++p == pe )
		goto _test_eof23;
case 23:
#line 562 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 32: goto tr8;
		case 34: goto st0;
		case 35: goto tr9;
		case 60: goto st0;
		case 62: goto st0;
		case 127: goto st0;
	}
	if ( 0 <= (*p) && (*p) <= 31 )
		goto st0;
	goto st23;
tr6:
#line 37 "ext/puma_http11/http11_parser.rl"
	{ MARK(mark, p); }
	goto st24;
st24:
	if ( ++p == pe )
		goto _test_eof24;
case 24:
#line 582 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 32: goto tr37;
		case 34: goto st0;
		case 35: goto tr38;
		case 60: goto st0;
		case 62: goto st0;
		case 63: goto tr39;
		case 127: goto st0;
	}
	if ( 0 <= (*p) && (*p) <= 31 )
		goto st0;
	goto st24;
tr39:
#line 69 "ext/puma_http11/http11_parser.rl"
	{
    parser->request_path(parser, PTR_TO(mark), LEN(mark,p));
  }
	goto st25;
st25:
	if ( ++p == pe )
		goto _test_eof25;
case 25:
#line 605 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 32: goto tr41;
		case 34: goto st0;
		case 35: goto tr42;
		case 60: goto st0;
		case 62: goto st0;
		case 127: goto st0;
	}
	if ( 0 <= (*p) && (*p) <= 31 )
		goto st0;
	goto tr40;
tr40:
#line 60 "ext/puma_http11/http11_parser.rl"
	{ MARK(query_start, p); }
	goto st26;
st26:
	if ( ++p == pe )
		goto _test_eof26;
case 26:
#line 625 "ext/puma_http11/http11_parser.c"
	switch( (*p) ) {
		case 32: goto tr44;
		case 34: goto st0;
		case 35: goto tr45;
		case 60: goto st0;
		case 62: goto st0;
		case 127: goto st0;
	}
	if ( 0 <= (*p) && (*p) <= 31 )
		goto st0;
	goto st26;
st27:
	if ( ++p == pe )
		goto _test_eof27;
case 27:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st28;
		case 95: goto st28;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st28;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st28;
	} else
		goto st28;
	goto st0;
st28:
	if ( ++p == pe )
		goto _test_eof28;
case 28:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st29;
		case 95: goto st29;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st29;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st29;
	} else
		goto st29;
	goto st0;
st29:
	if ( ++p == pe )
		goto _test_eof29;
case 29:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st30;
		case 95: goto st30;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st30;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st30;
	} else
		goto st30;
	goto st0;
st30:
	if ( ++p == pe )
		goto _test_eof30;
case 30:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st31;
		case 95: goto st31;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st31;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st31;
	} else
		goto st31;
	goto st0;
st31:
	if ( ++p == pe )
		goto _test_eof31;
case 31:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st32;
		case 95: goto st32;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st32;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st32;
	} else
		goto st32;
	goto st0;
st32:
	if ( ++p == pe )
		goto _test_eof32;
case 32:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st33;
		case 95: goto st33;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st33;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st33;
	} else
		goto st33;
	goto st0;
st33:
	if ( ++p == pe )
		goto _test_eof33;
case 33:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st34;
		case 95: goto st34;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st34;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st34;
	} else
		goto st34;
	goto st0;
st34:
	if ( ++p == pe )
		goto _test_eof34;
case 34:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st35;
		case 95: goto st35;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st35;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st35;
	} else
		goto st35;
	goto st0;
st35:
	if ( ++p == pe )
		goto _test_eof35;
case 35:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st36;
		case 95: goto st36;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st36;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st36;
	} else
		goto st36;
	goto st0;
st36:
	if ( ++p == pe )
		goto _test_eof36;
case 36:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st37;
		case 95: goto st37;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st37;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st37;
	} else
		goto st37;
	goto st0;
st37:
	if ( ++p == pe )
		goto _test_eof37;
case 37:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st38;
		case 95: goto st38;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st38;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st38;
	} else
		goto st38;
	goto st0;
st38:
	if ( ++p == pe )
		goto _test_eof38;
case 38:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st39;
		case 95: goto st39;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st39;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st39;
	} else
		goto st39;
	goto st0;
st39:
	if ( ++p == pe )
		goto _test_eof39;
case 39:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st40;
		case 95: goto st40;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st40;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st40;
	} else
		goto st40;
	goto st0;
st40:
	if ( ++p == pe )
		goto _test_eof40;
case 40:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st41;
		case 95: goto st41;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st41;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st41;
	} else
		goto st41;
	goto st0;
st41:
	if ( ++p == pe )
		goto _test_eof41;
case 41:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st42;
		case 95: goto st42;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st42;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st42;
	} else
		goto st42;
	goto st0;
st42:
	if ( ++p == pe )
		goto _test_eof42;
case 42:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st43;
		case 95: goto st43;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st43;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st43;
	} else
		goto st43;
	goto st0;
st43:
	if ( ++p == pe )
		goto _test_eof43;
case 43:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st44;
		case 95: goto st44;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st44;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st44;
	} else
		goto st44;
	goto st0;
st44:
	if ( ++p == pe )
		goto _test_eof44;
case 44:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st45;
		case 95: goto st45;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st45;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st45;
	} else
		goto st45;
	goto st0;
st45:
	if ( ++p == pe )
		goto _test_eof45;
case 45:
	if ( (*p) == 32 )
		goto tr2;
	goto st0;
	}
	_test_eof2: cs = 2; goto _test_eof;
	_test_eof3: cs = 3; goto _test_eof;
	_test_eof4: cs = 4; goto _test_eof;
	_test_eof5: cs = 5; goto _test_eof;
	_test_eof6: cs = 6; goto _test_eof;
	_test_eof7: cs = 7; goto _test_eof;
	_test_eof8: cs = 8; goto _test_eof;
	_test_eof9: cs = 9; goto _test_eof;
	_test_eof10: cs = 10; goto _test_eof;
	_test_eof11: cs = 11; goto _test_eof;
	_test_eof12: cs = 12; goto _test_eof;
	_test_eof13: cs = 13; goto _test_eof;
	_test_eof14: cs = 14; goto _test_eof;
	_test_eof15: cs = 15; goto _test_eof;
	_test_eof16: cs = 16; goto _test_eof;
	_test_eof46: cs = 46; goto _test_eof;
	_test_eof17: cs = 17; goto _test_eof;
	_test_eof18: cs = 18; goto _test_eof;
	_test_eof19: cs = 19; goto _test_eof;
	_test_eof20: cs = 20; goto _test_eof;
	_test_eof21: cs = 21; goto _test_eof;
	_test_eof22: cs = 22; goto _test_eof;
	_test_eof23: cs = 23; goto _test_eof;
	_test_eof24: cs = 24; goto _test_eof;
	_test_eof25: cs = 25; goto _test_eof;
	_test_eof26: cs = 26; goto _test_eof;
	_test_eof27: cs = 27; goto _test_eof;
	_test_eof28: cs = 28; goto _test_eof;
	_test_eof29: cs = 29; goto _test_eof;
	_test_eof30: cs = 30; goto _test_eof;
	_test_eof31: cs = 31; goto _test_eof;
	_test_eof32: cs = 32; goto _test_eof;
	_test_eof33: cs = 33; goto _test_eof;
	_test_eof34: cs = 34; goto _test_eof;
	_test_eof35: cs = 35; goto _test_eof;
	_test_eof36: cs = 36; goto _test_eof;
	_test_eof37: cs = 37; goto _test_eof;
	_test_eof38: cs = 38; goto _test_eof;
	_test_eof39: cs = 39; goto _test_eof;
	_test_eof40: cs = 40; goto _test_eof;
	_test_eof41: cs = 41; goto _test_eof;
	_test_eof42: cs = 42; goto _test_eof;
	_test_eof43: cs = 43; goto _test_eof;
	_test_eof44: cs = 44; goto _test_eof;
	_test_eof45: cs = 45; goto _test_eof;

	_test_eof: {}
	_out: {}
	}

#line 117 "ext/puma_http11/http11_parser.rl"

  if (!puma_parser_has_error(parser))
    parser->cs = cs;
  parser->nread += p - (buffer + off);

  assert(p <= pe && "buffer overflow after parsing execute");
  assert(parser->nread <= len && "nread longer than length");
  assert(parser->body_start <= len && "body starts after buffer end");
  assert(parser->mark < len && "mark is after buffer end");
  assert(parser->field_len <= len && "field has length longer than whole buffer");
  assert(parser->field_start < len && "field starts after buffer end");

  return(parser->nread);
}


libnsgif: Fix null pointer deref on frameless GIF input

A crafted GIF file with no frame data could cause a null pointer
dereference leading to denial of service (crash). Reported by
@JieyongMa via huntr.dev.

gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
{
        const unsigned char *gif_data;
        unsigned int index;
        gif_result return_value;

        /* Initialize values */
        gif->buffer_size = size;
        gif->gif_data = data;

        if (gif->lzw_ctx == NULL) {
                lzw_result res = lzw_context_create(
                                (struct lzw_ctx **)&gif->lzw_ctx);
                if (res != LZW_OK) {
                        return gif_error_from_lzw(res);
                }
        }

        /* Check for sufficient data to be a GIF (6-byte header + 7-byte
         * logical screen descriptor)
         */
        if (gif->buffer_size < GIF_STANDARD_HEADER_SIZE) {
                return GIF_INSUFFICIENT_DATA;
        }

        /* Get our current processing position */
        gif_data = gif->gif_data + gif->buffer_position;

        /* See if we should initialise the GIF */
        if (gif->buffer_position == 0) {
                /* We want everything to be NULL before we start so we've no
                 * chance of freeing bad pointers (paranoia)
                 */
                gif->frame_image = NULL;
                gif->frames = NULL;
                gif->local_colour_table = NULL;
                gif->global_colour_table = NULL;

                /* The caller may have been lazy and not reset any values */
                gif->frame_count = 0;
                gif->frame_count_partial = 0;
                gif->decoded_frame = GIF_INVALID_FRAME;

                /* 6-byte GIF file header is:
                 *
                 *	+0	3CHARS	Signature ('GIF')
                 *	+3	3CHARS	Version ('87a' or '89a')
                 */
                if (strncmp((const char *) gif_data, "GIF", 3) != 0) {
                        return GIF_DATA_ERROR;
                }
                gif_data += 3;

                /* Ensure GIF reports version 87a or 89a */
                /*
                if ((strncmp(gif_data, "87a", 3) != 0) &&
                    (strncmp(gif_data, "89a", 3) != 0))
                               LOG(("Unknown GIF format - proceeding anyway"));
                */
                gif_data += 3;

                /* 7-byte Logical Screen Descriptor is:
                 *
                 *	+0	SHORT	Logical Screen Width
                 *	+2	SHORT	Logical Screen Height
                 *	+4	CHAR	__Packed Fields__
                 *                      1BIT	Global Colour Table Flag
                 *                      3BITS	Colour Resolution
                 *                      1BIT	Sort Flag
                 *                      3BITS	Size of Global Colour Table
                 *	+5	CHAR	Background Colour Index
                 *	+6	CHAR	Pixel Aspect Ratio
                 */
                gif->width = gif_data[0] | (gif_data[1] << 8);
                gif->height = gif_data[2] | (gif_data[3] << 8);
                gif->global_colours = (gif_data[4] & GIF_COLOUR_TABLE_MASK);
                gif->colour_table_size = (2 << (gif_data[4] & GIF_COLOUR_TABLE_SIZE_MASK));
                gif->background_index = gif_data[5];
                gif->aspect_ratio = gif_data[6];
                gif->loop_count = 1;
                gif_data += 7;

                /* Some broken GIFs report the size as the screen size they
                 * were created in. As such, we detect for the common cases and
                 * set the sizes as 0 if they are found which results in the
                 * GIF being the maximum size of the frames.
                 */
                if (((gif->width == 640) && (gif->height == 480)) ||
                    ((gif->width == 640) && (gif->height == 512)) ||
                    ((gif->width == 800) && (gif->height == 600)) ||
                    ((gif->width == 1024) && (gif->height == 768)) ||
                    ((gif->width == 1280) && (gif->height == 1024)) ||
                    ((gif->width == 1600) && (gif->height == 1200)) ||
                    ((gif->width == 0) || (gif->height == 0)) ||
                    ((gif->width > 2048) || (gif->height > 2048))) {
                        gif->width = 1;
                        gif->height = 1;
                }

                /* Allocate some data irrespective of whether we've got any
                 * colour tables. We always get the maximum size in case a GIF
                 * is lying to us. It's far better to give the wrong colours
                 * than to trample over some memory somewhere.
                */
                gif->global_colour_table = calloc(GIF_MAX_COLOURS, sizeof(unsigned int));
                gif->local_colour_table = calloc(GIF_MAX_COLOURS, sizeof(unsigned int));
                if ((gif->global_colour_table == NULL) ||
                    (gif->local_colour_table == NULL)) {
                        gif_finalise(gif);
                        return GIF_INSUFFICIENT_MEMORY;
                }

                /* Set the first colour to a value that will never occur in
                 * reality so we know if we've processed it
                */
                gif->global_colour_table[0] = GIF_PROCESS_COLOURS;

                /* Check if the GIF has no frame data (13-byte header + 1-byte
                 * termination block) Although generally useless, the GIF
                 * specification does not expressly prohibit this
                 */
                if (gif->buffer_size == (GIF_STANDARD_HEADER_SIZE + 1)) {
                        if (gif_data[0] == GIF_TRAILER) {
                                return GIF_OK;
                        } else {
                                return GIF_INSUFFICIENT_DATA;
                        }
                }

                /* Initialise enough workspace for a frame */
                if ((gif->frames = (gif_frame *)malloc(sizeof(gif_frame))) == NULL) {
                        gif_finalise(gif);
                        return GIF_INSUFFICIENT_MEMORY;
                }
                gif->frame_holders = 1;

                /* Initialise the bitmap header */
                assert(gif->bitmap_callbacks.bitmap_create);
                gif->frame_image = gif->bitmap_callbacks.bitmap_create(gif->width, gif->height);
                if (gif->frame_image == NULL) {
                        gif_finalise(gif);
                        return GIF_INSUFFICIENT_MEMORY;
                }

                /* Remember we've done this now */
                gif->buffer_position = gif_data - gif->gif_data;
        }

        /*  Do the colour map if we haven't already. As the top byte is always
         *  0xff or 0x00 depending on the transparency we know if it's been
         *  filled in.
         */
        if (gif->global_colour_table[0] == GIF_PROCESS_COLOURS) {
                /* Check for a global colour map signified by bit 7 */
                if (gif->global_colours) {
                        if (gif->buffer_size < (gif->colour_table_size * 3 + GIF_STANDARD_HEADER_SIZE)) {
                                return GIF_INSUFFICIENT_DATA;
                        }
                        for (index = 0; index < gif->colour_table_size; index++) {
                                /* Gif colour map contents are r,g,b.
                                 *
                                 * We want to pack them bytewise into the
                                 * colour table, such that the red component
                                 * is in byte 0 and the alpha component is in
                                 * byte 3.
                                 */
                                unsigned char *entry = (unsigned char *) &gif->
                                                       global_colour_table[index];

                                entry[0] = gif_data[0];	/* r */
                                entry[1] = gif_data[1];	/* g */
                                entry[2] = gif_data[2];	/* b */
                                entry[3] = 0xff;	/* a */

                                gif_data += 3;
                        }
                        gif->buffer_position = (gif_data - gif->gif_data);
                } else {
                        /* Create a default colour table with the first two
                         * colours as black and white
                         */
                        unsigned int *entry = gif->global_colour_table;

                        entry[0] = 0x00000000;
                        /* Force Alpha channel to opaque */
                        ((unsigned char *) entry)[3] = 0xff;

                        entry[1] = 0xffffffff;
                }
        }

        /* Repeatedly try to initialise frames */
        while ((return_value = gif_initialise_frame(gif)) == GIF_WORKING);

        /* If there was a memory error tell the caller */
        if ((return_value == GIF_INSUFFICIENT_MEMORY) ||
            (return_value == GIF_DATA_ERROR)) {
                return return_value;
        }

        /* If we didn't have some frames then a GIF_INSUFFICIENT_DATA becomes a
         * GIF_INSUFFICIENT_FRAME_DATA
         */
        if ((return_value == GIF_INSUFFICIENT_DATA) &&
            (gif->frame_count_partial > 0)) {
                return GIF_INSUFFICIENT_FRAME_DATA;
        }

        /* Return how many we got */
        return return_value;
}

chafa: NULL Pointer Dereference in function gif_internal_decode_frame at libnsgif.c:599 allows attackers to cause a denial of service (crash) via a crafted input file. in GitHub repository hpjansson/chafa prior to 1.10.2. chafa: NULL Pointer Dereference in function gif_internal_decode_frame at libnsgif.c:599 allows attackers to cause a denial of service (crash) via a crafted input file.


static gif_result gif_error_from_lzw(lzw_result l_res)
{
        static const gif_result g_res[] = {
                [LZW_OK]        = GIF_OK,
                [LZW_OK_EOD]    = GIF_END_OF_FRAME,
                [LZW_NO_MEM]    = GIF_INSUFFICIENT_MEMORY,
                [LZW_NO_DATA]   = GIF_INSUFFICIENT_FRAME_DATA,
                [LZW_EOI_CODE]  = GIF_FRAME_DATA_ERROR,
                [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
                [LZW_BAD_CODE]  = GIF_FRAME_DATA_ERROR,
        };
        return g_res[l_res];
}


static gif_result gif_initialise_frame(gif_animation *gif)
{
        int frame;
        gif_frame *temp_buf;

        const unsigned char *gif_data, *gif_end;
        ssize_t gif_bytes;
        unsigned int flags = 0;
        unsigned int width, height, offset_x, offset_y;
        ssize_t block_size, colour_table_size;
        bool first_image = true;
        gif_result return_value;
        bool premature_eof = false;

        /* Get the frame to decode and our data position */
        frame = gif->frame_count;

        /* Get our buffer position etc. */
        gif_data = (const unsigned char *)(gif->gif_data + gif->buffer_position);
        gif_end = (const unsigned char *)(gif->gif_data + gif->buffer_size);
        gif_bytes = (gif_end - gif_data);

        /* Check if we've finished */
        if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
                return GIF_OK;
        }

        /* Check if there is enough data remaining. The shortest block of data
         * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
         * trailer
         */
        if (gif_bytes < 6) {
                return GIF_INSUFFICIENT_DATA;
        }

        /* We could theoretically get some junk data that gives us millions of
         * frames, so we ensure that we don't have a silly number
         */
        if (frame > 262143)
                return GIF_FRAME_DATA_ERROR;

        /* Get some memory to store our pointers in etc. */
        if ((int)gif->frame_holders <= frame) {
                /* Allocate more memory */
                temp_buf = (gif_frame *)realloc(gif->frames, (frame + 1) * sizeof(gif_frame));
                if (temp_buf == NULL) {
                        return GIF_INSUFFICIENT_MEMORY;
                }
                gif->frames = temp_buf;
                gif->frame_holders = frame + 1;
        }

        /* Store our frame pointer. We would do it when allocating except we
         * start off with one frame allocated so we can always use realloc.
         */
        gif->frames[frame].frame_pointer = gif->buffer_position;
        gif->frames[frame].display = false;
        gif->frames[frame].virgin = true;
        gif->frames[frame].disposal_method = 0;
        gif->frames[frame].transparency = false;
        gif->frames[frame].frame_delay = 0;
        gif->frames[frame].redraw_required = false;

        /* Invalidate any previous decoding we have of this frame */
        if (gif->decoded_frame == frame) {
                gif->decoded_frame = GIF_INVALID_FRAME;
        }

        /* We pretend to initialise the frames, but really we just skip over
         * all the data contained within. This is all basically a cut down
         * version of gif_decode_frame that doesn't have any of the LZW bits in
         * it.
         */

        /* Initialise any extensions */
        gif->buffer_position = gif_data - gif->gif_data;
        return_value = gif_initialise_frame_extensions(gif, frame);
        if (return_value != GIF_OK) {
                return return_value;
        }
        gif_data = (gif->gif_data + gif->buffer_position);
        gif_bytes = (gif_end - gif_data);

        /* Check if we've finished */
        if ((gif_bytes = (gif_end - gif_data)) < 1) {
                return GIF_INSUFFICIENT_FRAME_DATA;
        }

        if (gif_data[0] == GIF_TRAILER) {
                gif->buffer_position = (gif_data - gif->gif_data);
                gif->frame_count = frame + 1;
                return GIF_OK;
        }

        /* If we're not done, there should be an image descriptor */
        if (gif_data[0] != GIF_IMAGE_SEPARATOR) {
                return GIF_FRAME_DATA_ERROR;
        }

        /* Do some simple boundary checking */
        if (gif_bytes < 10) {
                return GIF_INSUFFICIENT_FRAME_DATA;
        }
        offset_x = gif_data[1] | (gif_data[2] << 8);
        offset_y = gif_data[3] | (gif_data[4] << 8);
        width = gif_data[5] | (gif_data[6] << 8);
        height = gif_data[7] | (gif_data[8] << 8);

        /* Set up the redraw characteristics. We have to check for extending
         * the area due to multi-image frames.
         */
        if (!first_image) {
                if (gif->frames[frame].redraw_x > offset_x) {
                        gif->frames[frame].redraw_width += (gif->frames[frame].redraw_x - offset_x);
                        gif->frames[frame].redraw_x = offset_x;
                }

                if (gif->frames[frame].redraw_y > offset_y) {
                        gif->frames[frame].redraw_height += (gif->frames[frame].redraw_y - offset_y);
                        gif->frames[frame].redraw_y = offset_y;
                }

                if ((offset_x + width) > (gif->frames[frame].redraw_x + gif->frames[frame].redraw_width)) {
                        gif->frames[frame].redraw_width = (offset_x + width) - gif->frames[frame].redraw_x;
                }

                if ((offset_y + height) > (gif->frames[frame].redraw_y + gif->frames[frame].redraw_height)) {
                        gif->frames[frame].redraw_height = (offset_y + height) - gif->frames[frame].redraw_y;
                }
        } else {
                first_image = false;
                gif->frames[frame].redraw_x = offset_x;
                gif->frames[frame].redraw_y = offset_y;
                gif->frames[frame].redraw_width = width;
                gif->frames[frame].redraw_height = height;
        }

        /* if we are clearing the background then we need to redraw enough to
         * cover the previous frame too
         */
        gif->frames[frame].redraw_required = ((gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) ||
                                                (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE));

        /* Boundary checking - shouldn't ever happen except with junk data */
        if (gif_initialise_sprite(gif, (offset_x + width), (offset_y + height))) {
                return GIF_INSUFFICIENT_MEMORY;
        }

        /* Decode the flags */
        flags = gif_data[9];
        colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK);

        /* Move our data onwards and remember we've got a bit of this frame */
        gif_data += 10;
        gif_bytes = (gif_end - gif_data);
        gif->frame_count_partial = frame + 1;

        /* Skip the local colour table */
        if (flags & GIF_COLOUR_TABLE_MASK) {
                gif_data += 3 * colour_table_size;
                if ((gif_bytes = (gif_end - gif_data)) < 0) {
                        return GIF_INSUFFICIENT_FRAME_DATA;
                }
        }

        /* Ensure we have a correct code size */
        if (gif_bytes < 1) {
                return GIF_INSUFFICIENT_FRAME_DATA;
        }
        if (gif_data[0] > LZW_CODE_MAX) {
                return GIF_DATA_ERROR;
        }

        /* Move our pointer to the actual image data */
        gif_data++;
        --gif_bytes;

        /* Repeatedly skip blocks until we get a zero block or run out of data
         * These blocks of image data are processed later by gif_decode_frame()
         */
        block_size = 0;
        while (block_size != 1) {
                if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
                block_size = gif_data[0] + 1;
                /* Check if the frame data runs off the end of the file	*/
                if ((ssize_t)(gif_bytes - block_size) < 0) {
                        /* Try to recover by signaling the end of the gif.
                         * Once we get garbage data, there is no logical way to
                         * determine where the next frame is.  It's probably
                         * better to partially load the gif than not at all.
                         */
                        if (gif_bytes >= 2) {
                                gif_bytes = 1;
                                ++gif_data;
                                premature_eof = true;
                                break;
                        } else {
                                return GIF_INSUFFICIENT_FRAME_DATA;
                        }
                } else {
                        gif_bytes -= block_size;
                        gif_data += block_size;
                }
        }

        /* Add the frame and set the display flag */
        gif->buffer_position = gif_data - gif->gif_data;
        gif->frame_count = frame + 1;
        gif->frames[frame].display = true;

        /* Check if we've finished */
        if (gif_bytes < 1) {
                return GIF_INSUFFICIENT_FRAME_DATA;
        } else {
                if (premature_eof || gif_data[0] == GIF_TRAILER) {
                        return GIF_OK;
                }
        }
        return GIF_WORKING;
}


gif_internal_decode_frame(gif_animation *gif,
                          unsigned int frame,
                          bool clear_image)
{
        unsigned int index = 0;
        const unsigned char *gif_data, *gif_end;
        ssize_t gif_bytes;
        unsigned int width, height, offset_x, offset_y;
        unsigned int flags, colour_table_size, interlace;
        unsigned int *colour_table;
        unsigned int *frame_data = 0;	// Set to 0 for no warnings
        unsigned int *frame_scanline;
        ssize_t save_buffer_position;
        unsigned int return_value = 0;
        unsigned int x, y, decode_y, burst_bytes;
        register unsigned char colour;

        /* If the GIF has no frame data, frame holders will not be allocated in
         * gif_initialise() */
        if (gif->frames == NULL) {
                return GIF_INSUFFICIENT_DATA;
        }

        /* Ensure this frame is supposed to be decoded */
        if (gif->frames[frame].display == false) {
                return GIF_OK;
        }

        /* Ensure the frame is in range to decode */
        if (frame > gif->frame_count_partial) {
                return GIF_INSUFFICIENT_DATA;
        }

        /* done if frame is already decoded */
        if ((!clear_image) &&
            ((int)frame == gif->decoded_frame)) {
                return GIF_OK;
        }

        /* Get the start of our frame data and the end of the GIF data */
        gif_data = gif->gif_data + gif->frames[frame].frame_pointer;
        gif_end = gif->gif_data + gif->buffer_size;
        gif_bytes = (gif_end - gif_data);

        /*
         * Ensure there is a minimal amount of data to proceed.  The shortest
         * block of data is a 10-byte image descriptor + 1-byte gif trailer
         */
        if (gif_bytes < 12) {
                return GIF_INSUFFICIENT_FRAME_DATA;
        }

        /* Save the buffer position */
        save_buffer_position = gif->buffer_position;
        gif->buffer_position = gif_data - gif->gif_data;

        /* Skip any extensions because they have allready been processed */
        if ((return_value = gif_skip_frame_extensions(gif)) != GIF_OK) {
                goto gif_decode_frame_exit;
        }
        gif_data = (gif->gif_data + gif->buffer_position);
        gif_bytes = (gif_end - gif_data);

        /* Ensure we have enough data for the 10-byte image descriptor + 1-byte
         * gif trailer
         */
        if (gif_bytes < 12) {
                return_value = GIF_INSUFFICIENT_FRAME_DATA;
                goto gif_decode_frame_exit;
        }

        /* 10-byte Image Descriptor is:
         *
         *	+0	CHAR	Image Separator (0x2c)
         *	+1	SHORT	Image Left Position
         *	+3	SHORT	Image Top Position
         *	+5	SHORT	Width
         *	+7	SHORT	Height
         *	+9	CHAR	__Packed Fields__
         *			1BIT	Local Colour Table Flag
         *			1BIT	Interlace Flag
         *			1BIT	Sort Flag
         *			2BITS	Reserved
         *			3BITS	Size of Local Colour Table
         */
        if (gif_data[0] != GIF_IMAGE_SEPARATOR) {
                return_value = GIF_DATA_ERROR;
                goto gif_decode_frame_exit;
        }
        offset_x = gif_data[1] | (gif_data[2] << 8);
        offset_y = gif_data[3] | (gif_data[4] << 8);
        width = gif_data[5] | (gif_data[6] << 8);
        height = gif_data[7] | (gif_data[8] << 8);

        /* Boundary checking - shouldn't ever happen except unless the data has
         * been modified since initialisation.
         */
        if ((offset_x + width > gif->width) ||
            (offset_y + height > gif->height)) {
                return_value = GIF_DATA_ERROR;
                goto gif_decode_frame_exit;
        }

        /* Decode the flags */
        flags = gif_data[9];
        colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK);
        interlace = flags & GIF_INTERLACE_MASK;

        /* Advance data pointer to next block either colour table or image
         * data.
         */
        gif_data += 10;
        gif_bytes = (gif_end - gif_data);

        /* Set up the colour table */
        if (flags & GIF_COLOUR_TABLE_MASK) {
                if (gif_bytes < (int)(3 * colour_table_size)) {
                        return_value = GIF_INSUFFICIENT_FRAME_DATA;
                        goto gif_decode_frame_exit;
                }
                colour_table = gif->local_colour_table;
                if (!clear_image) {
                        for (index = 0; index < colour_table_size; index++) {
                                /* Gif colour map contents are r,g,b.
                                 *
                                 * We want to pack them bytewise into the
                                 * colour table, such that the red component
                                 * is in byte 0 and the alpha component is in
                                 * byte 3.
                                 */
                                unsigned char *entry =
                                        (unsigned char *) &colour_table[index];

                                entry[0] = gif_data[0];	/* r */
                                entry[1] = gif_data[1];	/* g */
                                entry[2] = gif_data[2];	/* b */
                                entry[3] = 0xff;	/* a */

                                gif_data += 3;
                        }
                } else {
                        gif_data += 3 * colour_table_size;
                }
                gif_bytes = (gif_end - gif_data);
        } else {
                colour_table = gif->global_colour_table;
        }

        /* Ensure sufficient data remains */
        if (gif_bytes < 1) {
                return_value = GIF_INSUFFICIENT_FRAME_DATA;
                goto gif_decode_frame_exit;
        }

        /* check for an end marker */
        if (gif_data[0] == GIF_TRAILER) {
                return_value = GIF_OK;
                goto gif_decode_frame_exit;
        }

        /* Get the frame data */
        assert(gif->bitmap_callbacks.bitmap_get_buffer);
        frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
        if (!frame_data) {
                return GIF_INSUFFICIENT_MEMORY;
        }

        /* If we are clearing the image we just clear, if not decode */
        if (!clear_image) {
                lzw_result res;
                const uint8_t *stack_base;
                const uint8_t *stack_pos;

                /* Ensure we have enough data for a 1-byte LZW code size +
                 * 1-byte gif trailer
                 */
                if (gif_bytes < 2) {
                        return_value = GIF_INSUFFICIENT_FRAME_DATA;
                        goto gif_decode_frame_exit;
                }

                /* If we only have a 1-byte LZW code size + 1-byte gif trailer,
                 * we're finished
                 */
                if ((gif_bytes == 2) && (gif_data[1] == GIF_TRAILER)) {
                        return_value = GIF_OK;
                        goto gif_decode_frame_exit;
                }

                /* If the previous frame's disposal method requires we restore
                 * the background colour or this is the first frame, clear
                 * the frame data
                 */
                if ((frame == 0) || (gif->decoded_frame == GIF_INVALID_FRAME)) {
                        memset((char*)frame_data,
                               GIF_TRANSPARENT_COLOUR,
                               gif->width * gif->height * sizeof(int));
                        gif->decoded_frame = frame;
                        /* The line below would fill the image with its
                         * background color, but because GIFs support
                         * transparency we likely wouldn't want to do that. */
                        /* memset((char*)frame_data, colour_table[gif->background_index], gif->width * gif->height * sizeof(int)); */
                } else if ((frame != 0) &&
                           (gif->frames[frame - 1].disposal_method == GIF_FRAME_CLEAR)) {
                        return_value = gif_internal_decode_frame(gif,
                                                                 (frame - 1),
                                                                 true);
                        if (return_value != GIF_OK) {
                                goto gif_decode_frame_exit;
                        }

                } else if ((frame != 0) &&
                           (gif->frames[frame - 1].disposal_method == GIF_FRAME_RESTORE)) {
                        /*
                         * If the previous frame's disposal method requires we
                         * restore the previous image, find the last image set
                         * to "do not dispose" and get that frame data
                         */
                        int last_undisposed_frame = frame - 2;
                        while ((last_undisposed_frame >= 0) &&
                               (gif->frames[last_undisposed_frame].disposal_method == GIF_FRAME_RESTORE)) {
                                last_undisposed_frame--;
                        }

                        /* If we don't find one, clear the frame data */
                        if (last_undisposed_frame == -1) {
                                /* see notes above on transparency
                                 * vs. background color
                                 */
                                memset((char*)frame_data,
                                       GIF_TRANSPARENT_COLOUR,
                                       gif->width * gif->height * sizeof(int));
                        } else {
                                return_value = gif_internal_decode_frame(gif, last_undisposed_frame, false);
                                if (return_value != GIF_OK) {
                                        goto gif_decode_frame_exit;
                                }
                                /* Get this frame's data */
                                assert(gif->bitmap_callbacks.bitmap_get_buffer);
                                frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
                                if (!frame_data) {
                                        return GIF_INSUFFICIENT_MEMORY;
                                }
                        }
                }
                gif->decoded_frame = frame;
                gif->buffer_position = (gif_data - gif->gif_data) + 1;

                /* Initialise the LZW decoding */
                res = lzw_decode_init(gif->lzw_ctx, gif->gif_data,
                                gif->buffer_size, gif->buffer_position,
                                gif_data[0], &stack_base, &stack_pos);
                if (res != LZW_OK) {
                        return gif_error_from_lzw(res);
                }

                /* Decompress the data */
                for (y = 0; y < height; y++) {
                        if (interlace) {
                                decode_y = gif_interlaced_line(height, y) + offset_y;
                        } else {
                                decode_y = y + offset_y;
                        }
                        frame_scanline = frame_data + offset_x + (decode_y * gif->width);

                        /* Rather than decoding pixel by pixel, we try to burst
                         * out streams of data to remove the need for end-of
                         * data checks every pixel.
                         */
                        x = width;
                        while (x > 0) {
                                burst_bytes = (stack_pos - stack_base);
                                if (burst_bytes > 0) {
                                        if (burst_bytes > x) {
                                                burst_bytes = x;
                                        }
                                        x -= burst_bytes;
                                        while (burst_bytes-- > 0) {
                                                colour = *--stack_pos;
                                                if (((gif->frames[frame].transparency) &&
                                                     (colour != gif->frames[frame].transparency_index)) ||
                                                    (!gif->frames[frame].transparency)) {
                                                        *frame_scanline = colour_table[colour];
                                                }
                                                frame_scanline++;
                                        }
                                } else {
                                        res = lzw_decode(gif->lzw_ctx, &stack_pos);
                                        if (res != LZW_OK) {
                                                /* Unexpected end of frame, try to recover */
                                                if (res == LZW_OK_EOD) {
                                                        return_value = GIF_OK;
                                                } else {
                                                        return_value = gif_error_from_lzw(res);
                                                }
                                                goto gif_decode_frame_exit;
                                        }
                                }
                        }
                }
        } else {
                /* Clear our frame */
                if (gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) {
                        for (y = 0; y < height; y++) {
                                frame_scanline = frame_data + offset_x + ((offset_y + y) * gif->width);
                                if (gif->frames[frame].transparency) {
                                        memset(frame_scanline,
                                               GIF_TRANSPARENT_COLOUR,
                                               width * 4);
                                } else {
                                        memset(frame_scanline,
                                               colour_table[gif->background_index],
                                               width * 4);
                                }
                        }
                }
        }
gif_decode_frame_exit:

        /* Check if we should test for optimisation */
        if (gif->frames[frame].virgin) {
                if (gif->bitmap_callbacks.bitmap_test_opaque) {
                        gif->frames[frame].opaque = gif->bitmap_callbacks.bitmap_test_opaque(gif->frame_image);
                } else {
                        gif->frames[frame].opaque = false;
                }
                gif->frames[frame].virgin = false;
        }

        if (gif->bitmap_callbacks.bitmap_set_opaque) {
                gif->bitmap_callbacks.bitmap_set_opaque(gif->frame_image, gif->frames[frame].opaque);
        }

        if (gif->bitmap_callbacks.bitmap_modified) {
                gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
        }

        /* Restore the buffer position */
        gif->buffer_position = save_buffer_position;

        return return_value;
}


gif_result gif_decode_frame(gif_animation *gif, unsigned int frame)
{
        return gif_internal_decode_frame(gif, frame, false);
}


void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
{
        memset(gif, 0, sizeof(gif_animation));
        gif->bitmap_callbacks = *bitmap_callbacks;
        gif->decoded_frame = GIF_INVALID_FRAME;
}


static unsigned int gif_interlaced_line(int height, int y) {
        if ((y << 3) < height) {
                return (y << 3);
        }
        y -= ((height + 7) >> 3);
        if ((y << 3) < (height - 4)) {
                return (y << 3) + 4;
        }
        y -= ((height + 3) >> 3);
        if ((y << 2) < (height - 2)) {
                return (y << 2) + 2;
        }
        y -= ((height + 1) >> 2);
        return (y << 1) + 1;
}


gif_initialise_sprite(gif_animation *gif,
                      unsigned int width,
                      unsigned int height)
{
        unsigned int max_width;
        unsigned int max_height;
        struct bitmap *buffer;

        /* Check if we've changed */
        if ((width <= gif->width) && (height <= gif->height)) {
                return GIF_OK;
        }

        /* Get our maximum values */
        max_width = (width > gif->width) ? width : gif->width;
        max_height = (height > gif->height) ? height : gif->height;

        /* Allocate some more memory */
        assert(gif->bitmap_callbacks.bitmap_create);
        buffer = gif->bitmap_callbacks.bitmap_create(max_width, max_height);
        if (buffer == NULL) {
                return GIF_INSUFFICIENT_MEMORY;
        }

        assert(gif->bitmap_callbacks.bitmap_destroy);
        gif->bitmap_callbacks.bitmap_destroy(gif->frame_image);
        gif->frame_image = buffer;
        gif->width = max_width;
        gif->height = max_height;

        /* Invalidate our currently decoded image */
        gif->decoded_frame = GIF_INVALID_FRAME;
        return GIF_OK;
}


gif_initialise_frame_extensions(gif_animation *gif, const int frame)
{
        const unsigned char *gif_data, *gif_end;
        ssize_t gif_bytes;
        ssize_t block_size;

        /* Get our buffer position etc.	*/
        gif_data = (const unsigned char *)(gif->gif_data + gif->buffer_position);
        gif_end = (const unsigned char *)(gif->gif_data + gif->buffer_size);

        /* Initialise the extensions */
        while (gif_data < gif_end && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
                ++gif_data;
                if ((gif_bytes = (gif_end - gif_data)) < 1) {
                        return GIF_INSUFFICIENT_FRAME_DATA;
                }

                /* Switch on extension label */
                switch (gif_data[0]) {
                case GIF_EXTENSION_GRAPHIC_CONTROL:
                        /* 6-byte Graphic Control Extension is:
                         *
                         *	+0	CHAR	Graphic Control Label
                         *	+1	CHAR	Block Size
                         *	+2	CHAR	__Packed Fields__
                         *			3BITS	Reserved
                         *			3BITS	Disposal Method
                         *			1BIT	User Input Flag
                         *			1BIT	Transparent Color Flag
                         *	+3	SHORT	Delay Time
                         *	+5	CHAR	Transparent Color Index
                         */
                        if (gif_bytes < 6) {
                                return GIF_INSUFFICIENT_FRAME_DATA;
                        }

                        gif->frames[frame].frame_delay = gif_data[3] | (gif_data[4] << 8);
                        if (gif_data[2] & GIF_TRANSPARENCY_MASK) {
                                gif->frames[frame].transparency = true;
                                gif->frames[frame].transparency_index = gif_data[5];
                        }
                        gif->frames[frame].disposal_method = ((gif_data[2] & GIF_DISPOSAL_MASK) >> 2);
                        /* I have encountered documentation and GIFs in the
                         * wild that use 0x04 to restore the previous frame,
                         * rather than the officially documented 0x03.  I
                         * believe some (older?)  software may even actually
                         * export this way.  We handle this as a type of
                         * "quirks" mode.
                         */
                        if (gif->frames[frame].disposal_method == GIF_FRAME_QUIRKS_RESTORE) {
                                gif->frames[frame].disposal_method = GIF_FRAME_RESTORE;
                        }
                        gif_data += (2 + gif_data[1]);
                        break;

                case GIF_EXTENSION_APPLICATION:
                        /* 14-byte+ Application Extension is:
                         *
                         *	+0    CHAR    Application Extension Label
                         *	+1    CHAR    Block Size
                         *	+2    8CHARS  Application Identifier
                         *	+10   3CHARS  Appl. Authentication Code
                         *	+13   1-256   Application Data (Data sub-blocks)
                         */
                        if (gif_bytes < 17) {
                                return GIF_INSUFFICIENT_FRAME_DATA;
                        }
                        if ((gif_data[1] == 0x0b) &&
                            (strncmp((const char *) gif_data + 2,
                                     "NETSCAPE2.0", 11) == 0) &&
                            (gif_data[13] == 0x03) &&
                            (gif_data[14] == 0x01)) {
                                gif->loop_count = gif_data[15] | (gif_data[16] << 8);
                        }
                        gif_data += (2 + gif_data[1]);
                        break;

                case GIF_EXTENSION_COMMENT:
                        /* Move the pointer to the first data sub-block Skip 1
                         * byte for the extension label
                         */
                        ++gif_data;
                        break;

                default:
                        /* Move the pointer to the first data sub-block Skip 2
                         * bytes for the extension label and size fields Skip
                         * the extension size itself
                         */
                        if (gif_bytes < 2) {
                                return GIF_INSUFFICIENT_FRAME_DATA;
                        }
                        gif_data += (2 + gif_data[1]);
                }

                /* Repeatedly skip blocks until we get a zero block or run out
                 * of data This data is ignored by this gif decoder
                 */
                gif_bytes = (gif_end - gif_data);
                block_size = 0;
                while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
                        block_size = gif_data[0] + 1;
                        if ((gif_bytes -= block_size) < 0) {
                                return GIF_INSUFFICIENT_FRAME_DATA;
                        }
                        gif_data += block_size;
                }
                ++gif_data;
        }

        /* Set buffer position and return */
        gif->buffer_position = (gif_data - gif->gif_data);
        return GIF_OK;
}


static gif_result gif_skip_frame_extensions(gif_animation *gif)
{
        const unsigned char *gif_data, *gif_end;
        ssize_t gif_bytes;
        ssize_t block_size;

        /* Get our buffer position etc.	*/
        gif_data = (const unsigned char *)(gif->gif_data + gif->buffer_position);
        gif_end = (const unsigned char *)(gif->gif_data + gif->buffer_size);
        gif_bytes = (gif_end - gif_data);

        /* Skip the extensions */
        while (gif_data < gif_end && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
                ++gif_data;
                if (gif_data >= gif_end) {
                        return GIF_INSUFFICIENT_FRAME_DATA;
                }

                /* Switch on extension label */
                switch(gif_data[0]) {
                case GIF_EXTENSION_COMMENT:
                        /* Move the pointer to the first data sub-block
                         * 1 byte for the extension label
                         */
                        ++gif_data;
                        break;

                default:
                        /* Move the pointer to the first data sub-block 2 bytes
                         * for the extension label and size fields Skip the
                         * extension size itself
                         */
                        if (gif_data + 1 >= gif_end) {
                                return GIF_INSUFFICIENT_FRAME_DATA;
                        }
                        gif_data += (2 + gif_data[1]);
                }

                /* Repeatedly skip blocks until we get a zero block or run out
                 * of data This data is ignored by this gif decoder
                 */
                gif_bytes = (gif_end - gif_data);
                block_size = 0;
                while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
                        block_size = gif_data[0] + 1;
                        if ((gif_bytes -= block_size) < 0) {
                                return GIF_INSUFFICIENT_FRAME_DATA;
                        }
                        gif_data += block_size;
                }
                ++gif_data;
        }

        /* Set buffer position and return */
        gif->buffer_position = (gif_data - gif->gif_data);
        return GIF_OK;
}


void gif_finalise(gif_animation *gif)
{
        /* Release all our memory blocks */
        if (gif->frame_image) {
                assert(gif->bitmap_callbacks.bitmap_destroy);
                gif->bitmap_callbacks.bitmap_destroy(gif->frame_image);
        }

        gif->frame_image = NULL;
        free(gif->frames);
        gif->frames = NULL;
        free(gif->local_colour_table);
        gif->local_colour_table = NULL;
        free(gif->global_colour_table);
        gif->global_colour_table = NULL;

        lzw_context_destroy(gif->lzw_ctx);
        gif->lzw_ctx = NULL;
}


patch 9.0.0020: with some completion reading past end of string

Problem:    With some completion reading past end of string.
Solution:   Check the length of the string.

ins_compl_add_tv(typval_T *tv, int dir, int fast)
{
    char_u	*word;
    int		dup = FALSE;
    int		empty = FALSE;
    int		flags = fast ? CP_FAST : 0;
    char_u	*(cptext[CPT_COUNT]);
    typval_T	user_data;
    int		status;

    user_data.v_type = VAR_UNKNOWN;
    if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL)
    {
	word = dict_get_string(tv->vval.v_dict, (char_u *)"word", FALSE);
	cptext[CPT_ABBR] = dict_get_string(tv->vval.v_dict,
						     (char_u *)"abbr", FALSE);
	cptext[CPT_MENU] = dict_get_string(tv->vval.v_dict,
						     (char_u *)"menu", FALSE);
	cptext[CPT_KIND] = dict_get_string(tv->vval.v_dict,
						     (char_u *)"kind", FALSE);
	cptext[CPT_INFO] = dict_get_string(tv->vval.v_dict,
						     (char_u *)"info", FALSE);
	dict_get_tv(tv->vval.v_dict, (char_u *)"user_data", &user_data);
	if (dict_get_string(tv->vval.v_dict, (char_u *)"icase", FALSE) != NULL
			&& dict_get_number(tv->vval.v_dict, (char_u *)"icase"))
	    flags |= CP_ICASE;
	if (dict_get_string(tv->vval.v_dict, (char_u *)"dup", FALSE) != NULL)
	    dup = dict_get_number(tv->vval.v_dict, (char_u *)"dup");
	if (dict_get_string(tv->vval.v_dict, (char_u *)"empty", FALSE) != NULL)
	    empty = dict_get_number(tv->vval.v_dict, (char_u *)"empty");
	if (dict_get_string(tv->vval.v_dict, (char_u *)"equal", FALSE) != NULL
			&& dict_get_number(tv->vval.v_dict, (char_u *)"equal"))
	    flags |= CP_EQUAL;
    }
    else
    {
	word = tv_get_string_chk(tv);
	CLEAR_FIELD(cptext);
    }
    if (word == NULL || (!empty && *word == NUL))
    {
	clear_tv(&user_data);
	return FAIL;
    }
    status = ins_compl_add(word, -1, NULL, cptext, &user_data, dir, flags, dup);
    if (status != OK)
	clear_tv(&user_data);
    return status;
}

Out-of-bounds Read in GitHub repository vim/vim prior to 9.0.


Merge pull request from GHSA-7fw8-54cv-r7pm

PJ_DEF(void) pj_scan_get_quote( pj_scanner *scanner,
				int begin_quote, int end_quote,
				pj_str_t *out)
{
    char beg = (char)begin_quote;
    char end = (char)end_quote;
    pj_scan_get_quotes(scanner, &beg, &end, 1, out);
}

PJSIP is a free and open source multimedia communication library written in C language implementing standard based protocols such as SIP, SDP, RTP, STUN, TURN, and ICE. In versions 2.11.1 and prior, parsing an incoming SIP message that contains a malformed multipart can potentially cause out-of-bound read access. This issue affects all PJSIP users that accept SIP multipart. The patch is available as commit in the `master` branch. There are no known workarounds.


PJ_DEF(int) pj_scan_get_char( pj_scanner *scanner )
{
    register char *s = scanner->curptr;
    int chr;

    if (s >= scanner->end || !*s) {
	pj_scan_syntax_err(scanner);
	return 0;
    }

    chr = *s;

    ++s;
    scanner->curptr = s;
    if (PJ_SCAN_CHECK_EOF(s) && PJ_SCAN_IS_PROBABLY_SPACE(*s) &&
    	scanner->skip_ws)
    {
	pj_scan_skip_whitespace(scanner);
    }
    return chr;
}


PJ_DEF(void) pj_scan_skip_line( pj_scanner *scanner )
{
    char *s = pj_memchr(scanner->curptr, '\n', scanner->end - scanner->curptr);
    if (!s) {
	scanner->curptr = scanner->end;
    } else {
	scanner->curptr = scanner->start_line = s+1;
	scanner->line++;
   }
}


PJ_DEF(void) pj_scan_skip_whitespace( pj_scanner *scanner )
{
    register char *s = scanner->curptr;

    while (PJ_SCAN_IS_SPACE(*s)) {
	++s;
    }

    if (PJ_SCAN_IS_NEWLINE(*s) && (scanner->skip_ws & PJ_SCAN_AUTOSKIP_NEWLINE)) {
	for (;;) {
	    if (*s == '\r') {
		++s;
		if (*s == '\n') ++s;
		++scanner->line;
		scanner->curptr = scanner->start_line = s;
	    } else if (*s == '\n') {
		++s;
		++scanner->line;
		scanner->curptr = scanner->start_line = s;
	    } else if (PJ_SCAN_IS_SPACE(*s)) {
		do {
		    ++s;
		} while (PJ_SCAN_IS_SPACE(*s));
	    } else {
		break;
	    }
	}
    }

    if (PJ_SCAN_IS_NEWLINE(*s) && (scanner->skip_ws & PJ_SCAN_AUTOSKIP_WS_HEADER)==PJ_SCAN_AUTOSKIP_WS_HEADER) {
	/* Check for header continuation. */
	scanner->curptr = s;

	if (*s == '\r') {
	    ++s;
	}
	if (*s == '\n') {
	    ++s;
	}
	scanner->start_line = s;

	if (PJ_SCAN_IS_SPACE(*s)) {
	    register char *t = s;
	    do {
		++t;
	    } while (PJ_SCAN_IS_SPACE(*t));

	    ++scanner->line;
	    scanner->curptr = t;
	}
    } else {
	scanner->curptr = s;
    }
}


PJ_DEF(void) pj_cis_add_num(pj_cis_t *cis)
{
    pj_cis_add_range( cis, '0', '9'+1);
}


PJ_DEF(void) pj_scan_get_quotes(pj_scanner *scanner,
                                const char *begin_quote, const char *end_quote,
                                int qsize, pj_str_t *out)
{
    register char *s = scanner->curptr;
    int qpair = -1;
    int i;

    pj_assert(qsize > 0);

    /* Check and eat the begin_quote. */
    for (i = 0; i < qsize; ++i) {
	if (*s == begin_quote[i]) {
	    qpair = i;
	    break;
	}
    }
    if (qpair == -1) {
	pj_scan_syntax_err(scanner);
	return;
    }
    ++s;

    /* Loop until end_quote is found.
     */
    do {
	/* loop until end_quote is found. */
	while (PJ_SCAN_CHECK_EOF(s) && *s != '\n' && *s != end_quote[qpair]) {
	    ++s;
	}

	/* check that no backslash character precedes the end_quote. */
	if (*s == end_quote[qpair]) {
	    if (*(s-1) == '\\') {
		char *q = s-2;
		char *r = s-2;

		while (r != scanner->begin && *r == '\\') {
		    --r;
		}
		/* break from main loop if we have odd number of backslashes */
		if (((unsigned)(q-r) & 0x01) == 1) {
		    break;
		}
		++s;
	    } else {
		/* end_quote is not preceeded by backslash. break now. */
		break;
	    }
	} else {
	    /* loop ended by non-end_quote character. break now. */
	    break;
	}
    } while (1);

    /* Check and eat the end quote. */
    if (*s != end_quote[qpair]) {
	pj_scan_syntax_err(scanner);
	return;
    }
    ++s;

    pj_strset3(out, scanner->curptr, s);

    scanner->curptr = s;

    if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) {
	pj_scan_skip_whitespace(scanner);
    }
}


PJ_DEF(void) pj_scan_advance_n( pj_scanner *scanner,
				 unsigned N, pj_bool_t skip_ws)
{
    if (scanner->curptr + N > scanner->end) {
	pj_scan_syntax_err(scanner);
	return;
    }

    scanner->curptr += N;

    if (PJ_SCAN_IS_PROBABLY_SPACE(*scanner->curptr) && skip_ws) {
	pj_scan_skip_whitespace(scanner);
    }
}


PJ_DEF(void) pj_cis_add_cis( pj_cis_t *cis, const pj_cis_t *rhs)
{
    int i;
    for (i=0; i<256; ++i) {
	if (PJ_CIS_ISSET(rhs, i))
	    PJ_CIS_SET(cis, i);
    }
}


PJ_DEF(void) pj_scan_get_n( pj_scanner *scanner,
			    unsigned N, pj_str_t *out)
{
    if (scanner->curptr + N > scanner->end) {
	pj_scan_syntax_err(scanner);
	return;
    }

    pj_strset(out, scanner->curptr, N);

    scanner->curptr += N;

    if (PJ_SCAN_IS_PROBABLY_SPACE(*scanner->curptr) && scanner->skip_ws) {
	pj_scan_skip_whitespace(scanner);
    }
}


PJ_DEF(void) pj_scan_get_until_ch( pj_scanner *scanner,
				   int until_char, pj_str_t *out)
{
    register char *s = scanner->curptr;

    if (s >= scanner->end) {
	pj_scan_syntax_err(scanner);
	return;
    }

    while (PJ_SCAN_CHECK_EOF(s) && *s != until_char) {
	++s;
    }

    pj_strset3(out, scanner->curptr, s);

    scanner->curptr = s;

    if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) {
	pj_scan_skip_whitespace(scanner);
    }
}


PJ_DEF(void) pj_scan_init( pj_scanner *scanner, char *bufstart,
			   pj_size_t buflen, unsigned options,
			   pj_syn_err_func_ptr callback )
{
    PJ_CHECK_STACK();

    scanner->begin = scanner->curptr = bufstart;
    scanner->end = bufstart + buflen;
    scanner->line = 1;
    scanner->start_line = scanner->begin;
    scanner->callback = callback;
    scanner->skip_ws = options;

    if (scanner->skip_ws)
	pj_scan_skip_whitespace(scanner);
}


PJ_DEF(int) pj_scan_peek_n( pj_scanner *scanner,
			     pj_size_t len, pj_str_t *out)
{
    char *endpos = scanner->curptr + len;

    if (endpos > scanner->end) {
	pj_scan_syntax_err(scanner);
	return -1;
    }

    pj_strset(out, scanner->curptr, len);
    return *endpos;
}


PJ_DEF(void) pj_scan_get_newline( pj_scanner *scanner )
{
    if (!PJ_SCAN_IS_NEWLINE(*scanner->curptr)) {
	pj_scan_syntax_err(scanner);
	return;
    }

    if (*scanner->curptr == '\r') {
	++scanner->curptr;
    }
    if (*scanner->curptr == '\n') {
	++scanner->curptr;
    }

    ++scanner->line;
    scanner->start_line = scanner->curptr;

    /**
     * This probably is a bug, see PROTOS test #2480.
     * This would cause scanner to incorrectly eat two new lines, e.g.
     * when parsing:
     *
     *	Content-Length: 120\r\n
     *	\r\n
     *	<space><space><space>...
     *
     * When pj_scan_get_newline() is called to parse the first newline
     * in the Content-Length header, it will eat the second newline
     * too because it thinks that it's a header continuation.
     *
     * if (PJ_SCAN_IS_PROBABLY_SPACE(*scanner->curptr) && scanner->skip_ws) {
     *    pj_scan_skip_whitespace(scanner);
     * }
     */
}


PJ_DEF(void) pj_cis_del_range( pj_cis_t *cis, int cstart, int cend)
{
    while (cstart != cend) {
        PJ_CIS_CLR(cis, cstart);
        cstart++;
    }
}


PJ_DEF(void) pj_cis_invert( pj_cis_t *cis )
{
    unsigned i;
    /* Can not set zero. This is the requirement of the parser. */
    for (i=1; i<256; ++i) {
	if (PJ_CIS_ISSET(cis,i))
            PJ_CIS_CLR(cis,i);
        else
            PJ_CIS_SET(cis,i);
    }
}


PJ_DEF(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len)
{
    if (scanner->curptr + len > scanner->end) {
	pj_scan_syntax_err(scanner);
	return -1;
    }
    return pj_ansi_strnicmp(scanner->curptr, s, len);
}


PJ_DEF(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len)
{
    if (scanner->curptr + len > scanner->end) {
	pj_scan_syntax_err(scanner);
	return -1;
    }
    return strncmp(scanner->curptr, s, len);
}


PJ_DEF(void) pj_scan_get_until_chr( pj_scanner *scanner,
				     const char *until_spec, pj_str_t *out)
{
    register char *s = scanner->curptr;
    pj_size_t speclen;

    if (s >= scanner->end) {
	pj_scan_syntax_err(scanner);
	return;
    }

    speclen = strlen(until_spec);
    while (PJ_SCAN_CHECK_EOF(s) && !memchr(until_spec, *s, speclen)) {
	++s;
    }

    pj_strset3(out, scanner->curptr, s);

    scanner->curptr = s;

    if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) {
	pj_scan_skip_whitespace(scanner);
    }
}


PJ_DEF(void) pj_cis_add_range(pj_cis_t *cis, int cstart, int cend)
{
    /* Can not set zero. This is the requirement of the parser. */
    pj_assert(cstart > 0);

    while (cstart != cend) {
        PJ_CIS_SET(cis, cstart);
	++cstart;
    }
}


PJ_DEF(int) pj_scan_peek_until( pj_scanner *scanner,
				const pj_cis_t *spec,
				pj_str_t *out)
{
    register char *s = scanner->curptr;

    if (s >= scanner->end) {
	pj_scan_syntax_err(scanner);
	return -1;
    }

    while (PJ_SCAN_CHECK_EOF(s) && !pj_cis_match( spec, *s))
	++s;

    pj_strset3(out, scanner->curptr, s);
    return *s;
}


PJ_DEF(void) pj_scan_get( pj_scanner *scanner,
			  const pj_cis_t *spec, pj_str_t *out)
{
    register char *s = scanner->curptr;

    pj_assert(pj_cis_match(spec,0)==0);

    /* EOF is detected implicitly */
    if (!pj_cis_match(spec, *s)) {
	pj_scan_syntax_err(scanner);
	return;
    }

    do {
	++s;
    } while (pj_cis_match(spec, *s));
    /* No need to check EOF here (PJ_SCAN_CHECK_EOF(s)) because
     * buffer is NULL terminated and pj_cis_match(spec,0) should be
     * false.
     */

    pj_strset3(out, scanner->curptr, s);

    scanner->curptr = s;

    if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) {
	pj_scan_skip_whitespace(scanner);
    }
}


PJ_DEF(void) pj_cis_del_str( pj_cis_t *cis, const char *str)
{
    while (*str) {
        PJ_CIS_CLR(cis, *str);
	++str;
    }
}


static void pj_scan_syntax_err(pj_scanner *scanner)
{
    (*scanner->callback)(scanner);
}


PJ_DEF(void) pj_scan_get_until( pj_scanner *scanner,
				const pj_cis_t *spec, pj_str_t *out)
{
    register char *s = scanner->curptr;

    if (s >= scanner->end) {
	pj_scan_syntax_err(scanner);
	return;
    }

    while (PJ_SCAN_CHECK_EOF(s) && !pj_cis_match(spec, *s)) {
	++s;
    }

    pj_strset3(out, scanner->curptr, s);

    scanner->curptr = s;

    if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) {
	pj_scan_skip_whitespace(scanner);
    }
}


PJ_DEF(int) pj_scan_stricmp_alnum( pj_scanner *scanner, const char *s,
				   int len)
{
    if (scanner->curptr + len > scanner->end) {
	pj_scan_syntax_err(scanner);
	return -1;
    }
    return strnicmp_alnum(scanner->curptr, s, len);
}


PJ_DEF(void) pj_scan_save_state( const pj_scanner *scanner,
				 pj_scan_state *state)
{
    state->curptr = scanner->curptr;
    state->line = scanner->line;
    state->start_line = scanner->start_line;
}


PJ_DEF(void) pj_scan_restore_state( pj_scanner *scanner,
				    pj_scan_state *state)
{
    scanner->curptr = state->curptr;
    scanner->line = state->line;
    scanner->start_line = state->start_line;
}


PJ_DEF(void) pj_scan_get_unescape( pj_scanner *scanner,
				   const pj_cis_t *spec, pj_str_t *out)
{
    register char *s = scanner->curptr;
    char *dst = s;

    pj_assert(pj_cis_match(spec,0)==0);

    /* Must not match character '%' */
    pj_assert(pj_cis_match(spec,'%')==0);

    /* EOF is detected implicitly */
    if (!pj_cis_match(spec, *s) && *s != '%') {
	pj_scan_syntax_err(scanner);
	return;
    }

    out->ptr = s;
    do {
	if (*s == '%') {
	    if (s+3 <= scanner->end && pj_isxdigit(*(s+1)) &&
		pj_isxdigit(*(s+2)))
	    {
		*dst = (pj_uint8_t) ((pj_hex_digit_to_val(*(s+1)) << 4) +
				      pj_hex_digit_to_val(*(s+2)));
		++dst;
		s += 3;
	    } else {
		*dst++ = *s++;
		*dst++ = *s++;
		break;
	    }
	}

	if (pj_cis_match(spec, *s)) {
	    char *start = s;
	    do {
		++s;
	    } while (pj_cis_match(spec, *s));

	    if (dst != start) pj_memmove(dst, start, s-start);
	    dst += (s-start);
	}

    } while (*s == '%');

    scanner->curptr = s;
    out->slen = (dst - out->ptr);

    if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) {
	pj_scan_skip_whitespace(scanner);
    }
}


PJ_DEF(void) pj_cis_add_str( pj_cis_t *cis, const char *str)
{
    while (*str) {
        PJ_CIS_SET(cis, *str);
	++str;
    }
}


PJ_DEF(void) pj_cis_add_alpha(pj_cis_t *cis)
{
    pj_cis_add_range( cis, 'a', 'z'+1);
    pj_cis_add_range( cis, 'A', 'Z'+1);
}


PJ_DEF(int) pj_scan_peek( pj_scanner *scanner,
			  const pj_cis_t *spec, pj_str_t *out)
{
    register char *s = scanner->curptr;

    if (s >= scanner->end) {
	pj_scan_syntax_err(scanner);
	return -1;
    }

    /* Don't need to check EOF with PJ_SCAN_CHECK_EOF(s) */
    while (pj_cis_match(spec, *s))
	++s;

    pj_strset3(out, scanner->curptr, s);
    return *s;
}


PJ_DEF(void) pj_scan_fini( pj_scanner *scanner )
{
    PJ_CHECK_STACK();
    PJ_UNUSED_ARG(scanner);
}


Fix null deref in xnu.kernelcache ##crash

* Reported by @xshad3 via huntr.dev

static void r_rebase_info_free(RRebaseInfo *info) {
	if (!info) {
		return;
	}

	if (info->ranges) {
		R_FREE (info->ranges);
		info->ranges = NULL;
	}

	R_FREE (info);
}

NULL Pointer Dereference in GitHub repository radareorg/radare2 prior to 5.6.0.


static RList *sections(RBinFile *bf) {
	RList *ret = NULL;
	RBinObject *obj = bf ? bf->o : NULL;

	if (!obj || !obj->bin_obj || !(ret = r_list_newf ((RListFree)free))) {
		return NULL;
	}

	RKernelCacheObj *kobj = (RKernelCacheObj*) obj->bin_obj;
	ensure_kexts_initialized (kobj, bf);

	int iter;
	RKext *kext;
	r_kext_index_foreach (kobj->kexts, iter, kext) {
		ut8 magicbytes[4];

		r_buf_read_at (kobj->cache_buf, kext->range.offset, magicbytes, 4);
		int magic = r_read_le32 (magicbytes);
		switch (magic) {
		case MH_MAGIC_64:
			sections_from_mach0 (ret, kext->mach0, bf, kext->range.offset, kext->name, kobj);
			break;
		default:
			eprintf ("Unknown sub-bin\n");
			break;
		}
	}

	sections_from_mach0 (ret, kobj->mach0, bf, 0, NULL, kobj);

	struct MACH0_(segment_command) *seg;
	int nsegs = R_MIN (kobj->mach0->nsegs, 128);
	int i;
	for (i = 0; i < nsegs; i++) {
		RBinSection *ptr;
		char segname[17];

		if (!(ptr = R_NEW0 (RBinSection))) {
			break;
		}

		seg = &kobj->mach0->segs[i];
		r_str_ncpy (segname, seg->segname, 17);
		r_str_filter (segname, -1);
		ptr->name = r_str_newf ("%d.%s", i, segname);
		ptr->size = seg->vmsize;
		ptr->vsize = seg->vmsize;
		ptr->paddr = seg->fileoff + bf->o->boffset;
		ptr->vaddr = seg->vmaddr;
		ptr->add = true;
		ptr->is_segment = true;
		if (!ptr->vaddr) {
			ptr->vaddr = ptr->paddr;
		}
		ptr->perm = prot2perm (seg->initprot);
		r_list_append (ret, ptr);
	}

	return ret;
}


static int prot2perm(int x) {
	int r = 0;
	if (x&1) r |= 4;
	if (x&2) r |= 2;
	if (x&4) r |= 1;
	return r;
}


static void r_kext_index_free(RKextIndex *index) {
	if (!index) {
		return;
	}

	int i = 0;
	RKext *kext;
	r_kext_index_foreach (index, i, kext) {
		r_kext_free (kext);
		index->entries[i] = NULL;
	}

	index->length = 0;
	R_FREE (index);
}


static ut64 baddr(RBinFile *bf) {
	if (!bf || !bf->o || !bf->o->bin_obj) {
		return 8LL;
	}

	RKernelCacheObj *obj = (RKernelCacheObj*) bf->o->bin_obj;
	return MACH0_(get_baddr)(obj->mach0);
}


static void process_kmod_init_term(RKernelCacheObj *obj, RKext *kext, RList *ret, ut64 **inits, ut64 **terms) {
	if (!*inits || !*terms) {
		struct section_t *sections = NULL;
		if (!(sections = MACH0_(get_sections) (obj->mach0))) {
			return;
		}

		int i = 0;
		for (; !sections[i].last; i++) {
			if (sections[i].size == 0) {
				continue;
			}

			ut64 start_paddr = 0;
			ut64 *target = NULL;
			int n_ptrs = 0;

			if (!*inits && strstr (sections[i].name, "__kmod_init")) {
				int n_inits = sections[i].size / 8;
				if (n_inits <= 0) {
					continue;
				}
				*inits = R_NEWS0 (ut64, n_inits + 1);
				target = *inits;
				n_ptrs = n_inits;
			}
			if (!*terms && strstr (sections[i].name, "__kmod_term")) {
				int n_terms = sections[i].size / 8;
				if (n_terms <= 0) {
					continue;
				}
				*terms = R_NEWS0 (ut64, n_terms + 1);
				target = *terms;
				n_ptrs = n_terms;
			}
			if (!target || !n_ptrs) {
				continue;
			}
			start_paddr = sections[i].offset;
			int j = 0;
			ut8 bytes[8];
			for (; j < n_ptrs; j++) {
				if (r_buf_read_at (obj->cache_buf, start_paddr + j * 8, bytes, 8) < 8) {
					break;
				}
				target[j] = K_RPTR (bytes);
			}
			target[j] = 0;
		}

		R_FREE (sections);
	}

	if (*inits) {
		create_initterm_syms (kext, ret, R_BIN_ENTRY_TYPE_INIT, *inits);
	}
	if (*terms) {
		create_initterm_syms (kext, ret, R_BIN_ENTRY_TYPE_FINI, *terms);
	}
}


static RList *entries(RBinFile *bf) {
	RList *ret;
	RBinObject *obj = bf ? bf->o : NULL;

	if (!obj || !obj->bin_obj || !(ret = r_list_newf (free))) {
		return NULL;
	}

	RKernelCacheObj *kobj = (RKernelCacheObj*) obj->bin_obj;
	ut64 entry_vaddr = kobj->mach0->entry;
	if (kobj->pa2va_exec <= entry_vaddr) {
		ut64 entry_paddr = entry_vaddr - kobj->pa2va_exec;
		RBinAddr *ba = newEntry (entry_paddr, entry_vaddr, 0);
		if (ba) {
			r_list_append (ret, ba);
		}
	}

	process_constructors (kobj, kobj->mach0, ret, 0, true, R_K_CONSTRUCTOR_TO_ENTRY, NULL);

	return ret;
}


static bool check_buffer(RBinFile *bf, RBuffer *b) {
	if (r_buf_size (b) > 4) {
		ut8 buf[4];
		r_buf_read_at (b, 0, buf, sizeof (buf));
		if (!memcmp (buf, "\xcf\xfa\xed\xfe", 4)) {
			return is_kernelcache_buffer (b);
		}
	}
	return false;
}


static RList *symbols(RBinFile *bf) {
	RList *ret = r_list_newf (free);
	if (!ret) {
		return NULL;
	}

	RKernelCacheObj *obj = (RKernelCacheObj*) bf->o->bin_obj;

	symbols_from_mach0 (ret, obj->mach0, bf, 0, 0);

	HtPP *kernel_syms_by_addr = sdb_ht_new ();
	if (!kernel_syms_by_addr) {
		r_list_free (ret);
		return NULL;
	}

	RListIter *iter;
	RBinSymbol *sym;
	ut64 enosys_addr = 0;
	r_list_foreach (ret, iter, sym) {
		r_strf_var (key, 64, "%"PFMT64x, sym->vaddr);
		sdb_ht_insert (kernel_syms_by_addr, key, sym->dname ? sym->dname : sym->name);
		if (!enosys_addr && strstr (sym->name, "enosys")) {
			enosys_addr = sym->vaddr;
		}
	}

	RList *syscalls = resolve_syscalls (obj, enosys_addr);
	if (syscalls) {
		r_list_foreach (syscalls, iter, sym) {
			r_strf_var (key, 32, "%"PFMT64x, sym->vaddr);
			sdb_ht_insert (kernel_syms_by_addr, key, sym->name);
			r_list_append (ret, sym);
		}
		syscalls->free = NULL;
		r_list_free (syscalls);
	}

	RList *subsystem = resolve_mig_subsystem (obj);
	if (subsystem) {
		r_list_foreach (subsystem, iter, sym) {
			r_strf_var (key, 64, "%"PFMT64x, sym->vaddr);
			sdb_ht_insert (kernel_syms_by_addr, key, sym->name);
			r_list_append (ret, sym);
		}
		subsystem->free = NULL;
		r_list_free (subsystem);
	}

	ensure_kexts_initialized (obj, bf);

	RKext *kext;
	int kiter;
	ut64 *inits = NULL;
	ut64 *terms = NULL;
	r_kext_index_foreach (obj->kexts, kiter, kext) {
		ut8 magicbytes[4];
		r_buf_read_at (obj->cache_buf, kext->range.offset, magicbytes, 4);
		int magic = r_read_le32 (magicbytes);
		switch (magic) {
		case MH_MAGIC_64:
			symbols_from_mach0 (ret, kext->mach0, bf, kext->range.offset, r_list_length (ret));
			symbols_from_stubs (ret, kernel_syms_by_addr, obj, bf, kext, r_list_length (ret));
			process_constructors (obj, kext->mach0, ret, kext->range.offset, false, R_K_CONSTRUCTOR_TO_SYMBOL, kext_short_name (kext));
			process_kmod_init_term (obj, kext, ret, &inits, &terms);

			break;
		default:
			eprintf ("Unknown sub-bin\n");
			break;
		}
	}

	R_FREE (inits);
	R_FREE (terms);

	sdb_ht_free (kernel_syms_by_addr);

	return ret;
}


static void r_kext_fill_text_range(RKext *kext) {
	struct section_t *sections = NULL;
	if (!(sections = MACH0_(get_sections) (kext->mach0))) {
		return;
	}

	int i = 0;
	for (; !sections[i].last; i++) {
		if (strstr (sections[i].name, "__TEXT_EXEC.__text")) {
			kext->text_range.offset = sections[i].offset;
			kext->text_range.size = sections[i].size;
			kext->vaddr = sections[i].addr;
			break;
		}
	}

	R_FREE (sections);
}


static void r_rebase_info_populate(RRebaseInfo *info, RKernelCacheObj *obj) {
	struct section_t *sections = NULL;
	int i = 0;

	if (obj->rebase_info_populated) {
		return;
	}
	obj->rebase_info_populated = true;

	for (; i < info->n_ranges; i++) {
		if (info->ranges[i].size != UT64_MAX) {
			goto cleanup;
		} else if (sections == NULL) {
			if (!(sections = MACH0_(get_sections) (obj->mach0))) {
				return;
			}
		}
		info->ranges[i].offset = r_rebase_offset_to_paddr (obj, sections, info->ranges[i].offset);
		ut64 end = iterate_rebase_list (obj->cache_buf, info->multiplier, info->ranges[i].offset, NULL, NULL);
		if (end != UT64_MAX) {
			info->ranges[i].size = end - info->ranges[i].offset + 8;
		} else {
			info->ranges[i].size = 0;
		}
	}

cleanup:
	R_FREE (sections);
}


static ut64 r_rebase_offset_to_paddr(RKernelCacheObj *obj, struct section_t *sections, ut64 offset) {
	ut64 vaddr = obj->rebase_info->kernel_base + offset;
	int i = 0;
	for (; !sections[i].last; i++) {
		if (sections[i].addr <= vaddr && vaddr < (sections[i].addr + sections[i].vsize)) {
			return sections[i].offset + (vaddr - sections[i].addr);
		}
	}
	return offset;
}


static void rebase_buffer_fixup(RKernelCacheObj *kobj, ut64 off, RIODesc *fd, ut8 *buf, int count) {
	if (kobj->rebasing_buffer) {
		return;
	}
	kobj->rebasing_buffer = true;
	struct MACH0_(obj_t) *obj = kobj->mach0;
	ut64 eob = off + count;
	size_t i = 0;
	for (; i < obj->nsegs; i++) {
		if (!obj->chained_starts[i]) {
			continue;
		}
		ut64 page_size = obj->chained_starts[i]->page_size;
		ut64 start = obj->segs[i].fileoff;
		ut64 end = start + obj->segs[i].filesize;
		if (end >= off && start <= eob) {
			ut64 page_idx = (R_MAX (start, off) - start) / page_size;
			ut64 page_end_idx = (R_MIN (eob, end) - start) / page_size;
			for (; page_idx <= page_end_idx; page_idx++) {
				if (page_idx >= obj->chained_starts[i]->page_count) {
					break;
				}
				ut16 page_start = obj->chained_starts[i]->page_start[page_idx];
				if (page_start == DYLD_CHAINED_PTR_START_NONE) {
					continue;
				}
				ut64 cursor = start + page_idx * page_size + page_start;
				while (cursor < eob && cursor < end) {
					ut8 tmp[8];
					if (r_buf_read_at (obj->b, cursor, tmp, 8) != 8) {
						break;
					}
					ut64 raw_ptr = r_read_le64 (tmp);
					ut64 ptr_value = raw_ptr;
					ut64 delta = 0;
					ut64 stride = 8;
					if (obj->chained_starts[i]->pointer_format == DYLD_CHAINED_PTR_ARM64E) {
						bool is_auth = IS_PTR_AUTH (raw_ptr);
						bool is_bind = IS_PTR_BIND (raw_ptr);
						if (is_auth && is_bind) {
							struct dyld_chained_ptr_arm64e_auth_bind *p =
									(struct dyld_chained_ptr_arm64e_auth_bind *) &raw_ptr;
							delta = p->next;
						} else if (!is_auth && is_bind) {
							struct dyld_chained_ptr_arm64e_bind *p =
									(struct dyld_chained_ptr_arm64e_bind *) &raw_ptr;
							delta = p->next;
						} else if (is_auth && !is_bind) {
							struct dyld_chained_ptr_arm64e_auth_rebase *p =
									(struct dyld_chained_ptr_arm64e_auth_rebase *) &raw_ptr;
							delta = p->next;
							ptr_value = p->target + obj->baddr;
						} else {
							struct dyld_chained_ptr_arm64e_rebase *p =
									(struct dyld_chained_ptr_arm64e_rebase *) &raw_ptr;
							delta = p->next;
							ptr_value = ((ut64)p->high8 << 56) | p->target;
							ptr_value += obj->baddr;
						}
					} else if (obj->chained_starts[i]->pointer_format == DYLD_CHAINED_PTR_64_KERNEL_CACHE ||
							obj->chained_starts[i]->pointer_format == DYLD_CHAINED_PTR_ARM64E_KERNEL) {
						bool is_auth = IS_PTR_AUTH (raw_ptr);
						stride = 4;
						if (is_auth) {
							struct dyld_chained_ptr_arm64e_cache_auth_rebase *p =
									(struct dyld_chained_ptr_arm64e_cache_auth_rebase *) &raw_ptr;
							delta = p->next;
							ptr_value = p->target + obj->baddr;
						} else {
							struct dyld_chained_ptr_arm64e_cache_rebase *p =
									(struct dyld_chained_ptr_arm64e_cache_rebase *) &raw_ptr;
							delta = p->next;
							ptr_value = ((ut64)p->high8 << 56) | p->target;
							ptr_value += obj->baddr;
						}
					} else {
						eprintf ("Unsupported pointer format: %u\n", obj->chained_starts[i]->pointer_format);
					}
					ut64 in_buf = cursor - off;
					if (cursor >= off && cursor <= eob - 8) {
						r_write_le64 (&buf[in_buf], ptr_value);
					}
					cursor += delta * stride;
					if (!delta) {
						break;
					}
				}
			}
		}
	}
	kobj->rebasing_buffer = false;
}


static RList *kexts_from_load_commands(RKernelCacheObj *obj, RBinFile *bf) {
	RList *kexts = r_list_newf ((RListFree) &r_kext_free);
	if (!kexts) {
		return NULL;
	}

	ut32 i, ncmds = r_buf_read_le32_at (obj->cache_buf, 16);
	ut64 length = r_buf_size (obj->cache_buf);

	ut32 cursor = sizeof (struct MACH0_(mach_header));
	for (i = 0; i < ncmds && cursor < length; i++) {
		ut32 cmdtype = r_buf_read_le32_at (obj->cache_buf, cursor);
		ut32 cmdsize = r_buf_read_le32_at (obj->cache_buf, cursor + 4);
		if (!cmdsize || cmdsize + cursor < cursor) {
			break;
		}
		if (cmdtype != LC_KEXT) {
			cursor += cmdsize;
			continue;
		}

		ut64 vaddr = r_buf_read_le64_at (obj->cache_buf, cursor + 8);
		ut64 paddr = r_buf_read_le64_at (obj->cache_buf, cursor + 16);
		st32 padded_name_length = (st32)cmdsize - 32;
		if (padded_name_length <= 0 || cmdsize - 32 + cursor >= length) {
			cursor += cmdsize;
			continue;
		}

		char *padded_name = calloc (1, padded_name_length);
		if (!padded_name) {
			goto beach;
		}
		if (r_buf_read_at (obj->cache_buf, cursor + 32, (ut8 *)padded_name, padded_name_length)
				!= padded_name_length) {
			free (padded_name);
			goto early;
		}

		RKext *kext = R_NEW0 (RKext);
		if (!kext) {
			free (padded_name);
			goto beach;
		}

		kext->vaddr = vaddr;
		kext->range.offset = paddr;

		kext->mach0 = create_kext_shared_mach0 (obj, kext, bf);
		if (!kext->mach0) {
			free (padded_name);
			r_kext_free (kext);
			cursor += cmdsize;
			continue;
		}

		r_kext_fill_text_range (kext);
		kext->vaddr = K_PPTR (kext->vaddr);
		kext->pa2va_exec = obj->pa2va_exec;
		kext->pa2va_data = obj->pa2va_data;
		kext->name = strdup (padded_name);
		kext->own_name = true;
		free (padded_name);
		r_list_push (kexts, kext);

		cursor += cmdsize;
	}
early:
	return kexts;
beach:
	r_list_free (kexts);
	return NULL;
}


static ut64 p_ptr(ut64 decorated_addr, RKernelCacheObj *obj) {
	RParsedPointer ptr;
	r_parse_pointer (&ptr, decorated_addr, obj);
	return ptr.address;
}


static void destroy(RBinFile *bf) {
	r_kernel_cache_free ((RKernelCacheObj*) bf->o->bin_obj);
}


static RList *resolve_mig_subsystem(RKernelCacheObj *obj) {
	struct section_t *sections = NULL;
	if (!(sections = MACH0_(get_sections) (obj->mach0))) {
		return NULL;
	}

	HtPP *mig_hash = NULL;
	RList *subsystem = NULL;
	ut8 *data_const = NULL;
	ut64 data_const_offset = 0, data_const_size = 0, data_const_vaddr = 0;
	ut64 text_exec_offset = 0, text_exec_size = 0, text_exec_vaddr = 0;
	int incomplete = 2;
	int i = 0;
	for (; !sections[i].last && incomplete > 0; i++) {
		if (strstr (sections[i].name, "__DATA_CONST.__const")) {
			data_const_offset = sections[i].offset;
			data_const_size = sections[i].size;
			data_const_vaddr = K_PPTR (sections[i].addr);
			incomplete--;
		}
		if (strstr (sections[i].name, "__TEXT_EXEC.__text")) {
			text_exec_offset = sections[i].offset;
			text_exec_size = sections[i].size;
			text_exec_vaddr = K_PPTR (sections[i].addr);
			incomplete--;
		}
	}

	if (!data_const_offset || !data_const_size || !data_const_vaddr ||
		!text_exec_offset || !text_exec_size || !text_exec_vaddr) {
		goto beach;
	}

	data_const = malloc (data_const_size);
	if (!data_const) {
		goto beach;
	}
	if (r_buf_read_at (obj->cache_buf, data_const_offset, data_const, data_const_size) < data_const_size) {
		goto beach;
	}

	subsystem = r_list_newf (r_bin_symbol_free);
	if (!subsystem) {
		goto beach;
	}

	mig_hash = mig_hash_new ();
	if (!mig_hash) {
		goto beach;
	}

	ut8 *cursor = data_const;
	ut8 *end = data_const + data_const_size;
	while (cursor < end) {
		ut64 subs_p = K_PPTR (r_read_le64 (cursor));
		if (subs_p < text_exec_vaddr || subs_p >= text_exec_vaddr + text_exec_size) {
			cursor += 8;
			continue;
		}
		ut32 subs_min_idx = r_read_le32 (cursor + 8);
		ut32 subs_max_idx = r_read_le32 (cursor + 12);
		if (subs_min_idx >= subs_max_idx || (subs_max_idx - subs_min_idx) > K_MIG_MAX_ROUTINES) {
			cursor += 16;
			continue;
		}

		ut32 n_routines = (subs_max_idx - subs_min_idx);
		ut64 *routines = (ut64 *) calloc (n_routines, sizeof (ut64));
		if (!routines) {
			goto beach;
		}
		ut8 *array_cursor = cursor + K_MIG_SUBSYSTEM_SIZE;
		ut8 *end_array = array_cursor + n_routines * K_MIG_ROUTINE_SIZE;
		bool is_consistent = true;
		int idx = 0;
		while (array_cursor < end_array) {
			ut64 should_be_null = r_read_le64 (array_cursor);
			if (should_be_null != 0) {
				is_consistent = false;
				break;
			}

			ut64 routine_p = K_PPTR (r_read_le64 (array_cursor + 8));
			if (routine_p != 0 && (routine_p < text_exec_vaddr || routine_p >= text_exec_vaddr + text_exec_size)) {
				is_consistent = false;
				break;
			}

			routines[idx++] = routine_p;
			array_cursor += K_MIG_ROUTINE_SIZE;
		}

		if (is_consistent) {
			for (idx = 0; idx < n_routines; idx++) {
				ut64 routine_p = routines[idx];
				if (!routine_p) {
					continue;
				}

				RBinSymbol *sym = R_NEW0 (RBinSymbol);
				if (!sym) {
					R_FREE (routines);
					goto beach;
				}

				int num = idx + subs_min_idx;
				bool found = false;
				r_strf_var (key, 32, "%d", num);
				const char *name = sdb_ht_find (mig_hash, key, &found);
				if (found && name && *name) {
					sym->name = r_str_newf ("mig.%d.%s", num, name);
				} else {
					sym->name = r_str_newf ("mig.%d", num);
				}

				sym->vaddr = routine_p;
				sym->paddr = sym->vaddr - text_exec_vaddr + text_exec_offset;
				sym->size = 0;
				sym->forwarder = "NONE";
				sym->bind = "GLOBAL";
				sym->type = "OBJECT";
				r_list_append (subsystem, sym);
			}

			cursor += K_MIG_SUBSYSTEM_SIZE + n_routines * K_MIG_ROUTINE_SIZE;
		} else {
			cursor += 8;
		}

		R_FREE (routines);
	}

	sdb_ht_free (mig_hash);
	R_FREE (data_const);
	R_FREE (sections);
	return subsystem;

beach:
	if (subsystem) {
		r_list_free (subsystem);
	}
	if (mig_hash) {
		sdb_ht_free (mig_hash);
	}
	R_FREE (data_const);
	R_FREE (sections);
	return NULL;
}