floppy: disable FDRAWCMD by default

Minh Yuan reported a concurrency use-after-free issue in the floppy code
between raw_cmd_ioctl and seek_interrupt.

[ It turns out this has been around, and that others have reported the
  KASAN splats over the years, but Minh Yuan had a reproducer for it and
  so gets primary credit for reporting it for this fix   - Linus ]

The problem is, this driver tends to break very easily and nowadays,
nobody is expected to use FDRAWCMD anyway since it was used to
manipulate non-standard formats.  The risk of breaking the driver is
higher than the risk presented by this race, and accessing the device
requires privileges anyway.

Let's just add a config option to completely disable this ioctl and
leave it disabled by default.  Distros shouldn't use it, and only those
running on antique hardware might need to enable it.

Link: https://lore.kernel.org/all/000000000000b71cdd05d703f6bf@google.com/
Link: https://lore.kernel.org/lkml/CAKcFiNC=MfYVW-Jt9A3=FPJpTwCD2PL_ULNCpsCVE5s8ZeBQgQ@mail.gmail.com
Link: https://lore.kernel.org/all/CAEAjamu1FRhz6StCe_55XY5s389ZP_xmCF69k987En+1z53=eg@mail.gmail.com
Reported-by: Minh Yuan <yuanmingbuaa@gmail.com>
Reported-by: syzbot+8e8958586909d62b6840@syzkaller.appspotmail.com
Reported-by: cruise k <cruise4k@gmail.com>
Reported-by: Kyungtae Kim <kt0755@gmail.com>
Suggested-by: Linus Torvalds <torvalds@linuxfoundation.org>
Tested-by: Denis Efremov <efremov@linux.com>
Signed-off-by: Willy Tarreau <w@1wt.eu>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
		    unsigned long param)
{
	int drive = (long)bdev->bd_disk->private_data;
	int type = ITYPE(drive_state[drive].fd_device);
	int i;
	int ret;
	int size;
	union inparam {
		struct floppy_struct g;	/* geometry */
		struct format_descr f;
		struct floppy_max_errors max_errors;
		struct floppy_drive_params dp;
	} inparam;		/* parameters coming from user space */
	const void *outparam;	/* parameters passed back to user space */

	/* convert compatibility eject ioctls into floppy eject ioctl.
	 * We do this in order to provide a means to eject floppy disks before
	 * installing the new fdutils package */
	if (cmd == CDROMEJECT ||	/* CD-ROM eject */
	    cmd == 0x6470) {		/* SunOS floppy eject */
		DPRINT("obsolete eject ioctl\n");
		DPRINT("please use floppycontrol --eject\n");
		cmd = FDEJECT;
	}

	if (!((cmd & 0xff00) == 0x0200))
		return -EINVAL;

	/* convert the old style command into a new style command */
	ret = normalize_ioctl(&cmd, &size);
	if (ret)
		return ret;

	/* permission checks */
	if (((cmd & 0x40) && !(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL))) ||
	    ((cmd & 0x80) && !capable(CAP_SYS_ADMIN)))
		return -EPERM;

	if (WARN_ON(size < 0 || size > sizeof(inparam)))
		return -EINVAL;

	/* copyin */
	memset(&inparam, 0, sizeof(inparam));
	if (_IOC_DIR(cmd) & _IOC_WRITE) {
		ret = fd_copyin((void __user *)param, &inparam, size);
		if (ret)
			return ret;
	}

	switch (cmd) {
	case FDEJECT:
		if (drive_state[drive].fd_ref != 1)
			/* somebody else has this drive open */
			return -EBUSY;
		if (lock_fdc(drive))
			return -EINTR;

		/* do the actual eject. Fails on
		 * non-Sparc architectures */
		ret = fd_eject(UNIT(drive));

		set_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
		set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
		process_fd_request();
		return ret;
	case FDCLRPRM:
		if (lock_fdc(drive))
			return -EINTR;
		current_type[drive] = NULL;
		floppy_sizes[drive] = MAX_DISK_SIZE << 1;
		drive_state[drive].keep_data = 0;
		return invalidate_drive(bdev);
	case FDSETPRM:
	case FDDEFPRM:
		return set_geometry(cmd, &inparam.g, drive, type, bdev);
	case FDGETPRM:
		ret = get_floppy_geometry(drive, type,
					  (struct floppy_struct **)&outparam);
		if (ret)
			return ret;
		memcpy(&inparam.g, outparam,
				offsetof(struct floppy_struct, name));
		outparam = &inparam.g;
		break;
	case FDMSGON:
		drive_params[drive].flags |= FTD_MSG;
		return 0;
	case FDMSGOFF:
		drive_params[drive].flags &= ~FTD_MSG;
		return 0;
	case FDFMTBEG:
		if (lock_fdc(drive))
			return -EINTR;
		if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
			return -EINTR;
		ret = drive_state[drive].flags;
		process_fd_request();
		if (ret & FD_VERIFY)
			return -ENODEV;
		if (!(ret & FD_DISK_WRITABLE))
			return -EROFS;
		return 0;
	case FDFMTTRK:
		if (drive_state[drive].fd_ref != 1)
			return -EBUSY;
		return do_format(drive, &inparam.f);
	case FDFMTEND:
	case FDFLUSH:
		if (lock_fdc(drive))
			return -EINTR;
		return invalidate_drive(bdev);
	case FDSETEMSGTRESH:
		drive_params[drive].max_errors.reporting = (unsigned short)(param & 0x0f);
		return 0;
	case FDGETMAXERRS:
		outparam = &drive_params[drive].max_errors;
		break;
	case FDSETMAXERRS:
		drive_params[drive].max_errors = inparam.max_errors;
		break;
	case FDGETDRVTYP:
		outparam = drive_name(type, drive);
		SUPBOUND(size, strlen((const char *)outparam) + 1);
		break;
	case FDSETDRVPRM:
		if (!valid_floppy_drive_params(inparam.dp.autodetect,
				inparam.dp.native_format))
			return -EINVAL;
		drive_params[drive] = inparam.dp;
		break;
	case FDGETDRVPRM:
		outparam = &drive_params[drive];
		break;
	case FDPOLLDRVSTAT:
		if (lock_fdc(drive))
			return -EINTR;
		if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
			return -EINTR;
		process_fd_request();
		fallthrough;
	case FDGETDRVSTAT:
		outparam = &drive_state[drive];
		break;
	case FDRESET:
		return user_reset_fdc(drive, (int)param, true);
	case FDGETFDCSTAT:
		outparam = &fdc_state[FDC(drive)];
		break;
	case FDWERRORCLR:
		memset(&write_errors[drive], 0, sizeof(write_errors[drive]));
		return 0;
	case FDWERRORGET:
		outparam = &write_errors[drive];
		break;
	case FDRAWCMD:
		if (type)
			return -EINVAL;
		if (lock_fdc(drive))
			return -EINTR;
		set_floppy(drive);
		i = raw_cmd_ioctl(cmd, (void __user *)param);
		if (i == -EINTR)
			return -EINTR;
		process_fd_request();
		return i;
	case FDTWADDLE:
		if (lock_fdc(drive))
			return -EINTR;
		twaddle(current_fdc, current_drive);
		process_fd_request();
		return 0;
	default:
		return -EINVAL;
	}

	if (_IOC_DIR(cmd) & _IOC_READ)
		return fd_copyout((void __user *)param, outparam, size);

	return 0;
}

Rejected reason: DO NOT USE THIS CANDIDATE NUMBER. ConsultIDs: CVE-2022-33981. Reason: This candidate is a reservation duplicate of CVE-2022-33981. Notes: All CVE users should reference CVE-2022-33981 instead of this candidate. All references and descriptions in this candidate have been removed to prevent accidental usage


RDMA/rtrs-clt: Fix possible double free in error case

Callback function rtrs_clt_dev_release() for put_device() calls kfree(clt)
to free memory. We shouldn't call kfree(clt) again, and we can't use the
clt after kfree too.

Replace device_register() with device_initialize() and device_add() so that
dev_set_name can() be used appropriately.

Move mutex_destroy() to the release function so it can be called in
the alloc_clt err path.

Fixes: eab098246625 ("RDMA/rtrs-clt: Refactor the failure cases in alloc_clt")
Link: https://lore.kernel.org/r/20220217030929.323849-1-haris.iqbal@ionos.com
Reported-by: Miaoqian Lin <linmq006@gmail.com>
Signed-off-by: Md Haris Iqbal <haris.iqbal@ionos.com>
Reviewed-by: Jack Wang <jinpu.wang@ionos.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>

static void rtrs_clt_dev_release(struct device *dev)
{
	struct rtrs_clt_sess *clt = container_of(dev, struct rtrs_clt_sess,
						 dev);

	kfree(clt);
}

drivers/infiniband/ulp/rtrs/rtrs-clt.c in the Linux kernel before 5.16.12 has a double free related to rtrs_clt_dev_release.


inbound_cap_ls (server *serv, char *nick, char *extensions_str,
					 const message_tags_data *tags_data)
{
	char buffer[256];	/* buffer for requesting capabilities and emitting the signal */
	guint32 want_cap; /* format the CAP REQ string based on previous capabilities being requested or not */
	guint32 want_sasl; /* CAP END shouldn't be sent when SASL is requested, it needs further responses */
	char **extensions;
	int i;

	EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPLIST, serv->server_session, nick,
								  extensions_str, NULL, NULL, 0, tags_data->timestamp);
	want_cap = 0;
	want_sasl = 0;

	extensions = g_strsplit (extensions_str, " ", 0);

	strcpy (buffer, "CAP REQ :");

	for (i=0; extensions[i]; i++)
	{
		const char *extension = extensions[i];

		if (!strcmp (extension, "identify-msg"))
		{
			strcat (buffer, "identify-msg ");
			want_cap = 1;
		}
		if (!strcmp (extension, "multi-prefix"))
		{
			strcat (buffer, "multi-prefix ");
			want_cap = 1;
		}
		if (!strcmp (extension, "away-notify"))
		{
			strcat (buffer, "away-notify ");
			want_cap = 1;
		}
		if (!strcmp (extension, "account-notify"))
		{
			strcat (buffer, "account-notify ");
			want_cap = 1;
		}
		if (!strcmp (extension, "extended-join"))
		{
			strcat (buffer, "extended-join ");
			want_cap = 1;
		}
		if (!strcmp (extension, "userhost-in-names"))
		{
			strcat (buffer, "userhost-in-names ");
			want_cap = 1;
		}

		/* bouncers can prefix a name space to the extension so we should use.
		 * znc <= 1.0 uses "znc.in/server-time" and newer use "znc.in/server-time-iso".
		 */
		if (!strcmp (extension, "znc.in/server-time-iso"))
		{
			strcat (buffer, "znc.in/server-time-iso ");
			want_cap = 1;
		}
		if (!strcmp (extension, "znc.in/server-time"))
		{
			strcat (buffer, "znc.in/server-time ");
			want_cap = 1;
		}
		if (prefs.hex_irc_cap_server_time
			 && !strcmp (extension, "server-time"))
		{
			strcat (buffer, "server-time ");
			want_cap = 1;
		}

		/* if the SASL password is set AND auth mode is set to SASL, request SASL auth */
		if (!strcmp (extension, "sasl")
			&& ((serv->loginmethod == LOGIN_SASL && strlen (serv->password) != 0)
			|| (serv->loginmethod == LOGIN_SASLEXTERNAL && serv->have_cert)))
		{
			strcat (buffer, "sasl ");
			want_cap = 1;
			want_sasl = 1;
		}
	}

	g_strfreev (extensions);

	if (want_cap)
	{
		/* buffer + 9 = emit buffer without "CAP REQ :" */
		EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPREQ, serv->server_session,
									  buffer + 9, NULL, NULL, NULL, 0,
									  tags_data->timestamp);
		tcp_sendf (serv, "%s\r\n", g_strchomp (buffer));
	}
	if (!want_sasl)
	{
		/* if we use SASL, CAP END is dealt via raw numerics */
		serv->sent_capend = TRUE;
		tcp_send_len (serv, "CAP END\r\n", 9);
	}
}

Directory traversal vulnerability in the client in HexChat 2.11.0 allows remote IRC servers to read or modify arbitrary files via a .. (dot dot) in the server name.


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

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 13: goto tr26;
		case 32: goto tr27;
	}
	goto tr25;
tr25:
#line 46 "ext/puma_http11/http11_parser.rl"
	{ MARK(mark, p); }
	goto st19;
st19:
	if ( ++p == pe )
		goto _test_eof19;
case 19:
#line 442 "ext/puma_http11/http11_parser.c"
	if ( (*p) == 13 )
		goto tr29;
	goto st19;
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 488 "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 509 "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 530 "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 555 "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 575 "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 598 "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 618 "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);
}

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`.


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_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;

        /* 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;
}

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.


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_stop(int c, int prev_mode, int retval)
{
    char_u	*ptr;
    int		want_cindent;

    // Get here when we have finished typing a sequence of ^N and
    // ^P or other completion characters in CTRL-X mode.  Free up
    // memory that was used, and make sure we can redo the insert.
    if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E)
    {
	// If any of the original typed text has been changed, eg when
	// ignorecase is set, we must add back-spaces to the redo
	// buffer.  We add as few as necessary to delete just the part
	// of the original text that has changed.
	// When using the longest match, edited the match or used
	// CTRL-E then don't use the current match.
	if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E)
	    ptr = compl_curr_match->cp_str;
	else
	    ptr = NULL;
	ins_compl_fixRedoBufForLeader(ptr);
    }

    want_cindent = (get_can_cindent() && cindent_on());

    // When completing whole lines: fix indent for 'cindent'.
    // Otherwise, break line if it's too long.
    if (compl_cont_mode == CTRL_X_WHOLE_LINE)
    {
	// re-indent the current line
	if (want_cindent)
	{
	    do_c_expr_indent();
	    want_cindent = FALSE;	// don't do it again
	}
    }
    else
    {
	int prev_col = curwin->w_cursor.col;

	// put the cursor on the last char, for 'tw' formatting
	if (prev_col > 0)
	    dec_cursor();
	// only format when something was inserted
	if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E)
	    insertchar(NUL, 0, -1);
	if (prev_col > 0
		&& ml_get_curline()[curwin->w_cursor.col] != NUL)
	    inc_cursor();
    }

    // If the popup menu is displayed pressing CTRL-Y means accepting
    // the selection without inserting anything.  When
    // compl_enter_selects is set the Enter key does the same.
    if ((c == Ctrl_Y || (compl_enter_selects
		    && (c == CAR || c == K_KENTER || c == NL)))
	    && pum_visible())
	retval = TRUE;

    // CTRL-E means completion is Ended, go back to the typed text.
    // but only do this, if the Popup is still visible
    if (c == Ctrl_E)
    {
	ins_compl_delete();
	if (compl_leader != NULL)
	    ins_bytes(compl_leader + get_compl_len());
	else if (compl_first_match != NULL)
	    ins_bytes(compl_orig_text + get_compl_len());
	retval = TRUE;
    }

    auto_format(FALSE, TRUE);

    // Trigger the CompleteDonePre event to give scripts a chance to
    // act upon the completion before clearing the info, and restore
    // ctrl_x_mode, so that complete_info() can be used.
    ctrl_x_mode = prev_mode;
    ins_apply_autocmds(EVENT_COMPLETEDONEPRE);

    ins_compl_free();
    compl_started = FALSE;
    compl_matches = 0;
    if (!shortmess(SHM_COMPLETIONMENU))
	msg_clr_cmdline();	// necessary for "noshowmode"
    ctrl_x_mode = CTRL_X_NORMAL;
    compl_enter_selects = FALSE;
    if (edit_submode != NULL)
    {
	edit_submode = NULL;
	showmode();
    }

#ifdef FEAT_CMDWIN
    if (c == Ctrl_C && cmdwin_type != 0)
	// Avoid the popup menu remains displayed when leaving the
	// command line window.
	update_screen(0);
#endif
    // Indent now if a key was typed that is in 'cinkeys'.
    if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0)))
	do_c_expr_indent();
    // Trigger the CompleteDone event to give scripts a chance to act
    // upon the end of completion.
    ins_apply_autocmds(EVENT_COMPLETEDONE);

    return retval;
}

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


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

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

    if (!chr) {
	pj_scan_syntax_err(scanner);
	return 0;
    }

    ++scanner->curptr;

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

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.


Fix null deref in xnu.kernelcache ##crash

* Reported by @xshad3 via huntr.dev

static bool load_buffer(RBinFile *bf, void **bin_obj, RBuffer *buf, ut64 loadaddr, Sdb *sdb) {
	RBuffer *fbuf = r_buf_ref (buf);
	struct MACH0_(opts_t) opts;
	MACH0_(opts_set_default) (&opts, bf);
	struct MACH0_(obj_t) *main_mach0 = MACH0_(new_buf) (fbuf, &opts);
	if (!main_mach0) {
		return false;
	}

	RRebaseInfo *rebase_info = r_rebase_info_new_from_mach0 (fbuf, main_mach0);
	RKernelCacheObj *obj = NULL;

	RPrelinkRange *prelink_range = get_prelink_info_range_from_mach0 (main_mach0);
	if (!prelink_range) {
		goto beach;
	}

	obj = R_NEW0 (RKernelCacheObj);
	if (!obj) {
		R_FREE (prelink_range);
		goto beach;
	}

	RCFValueDict *prelink_info = NULL;
	if (main_mach0->hdr.filetype != MH_FILESET && prelink_range->range.size) {
		prelink_info = r_cf_value_dict_parse (fbuf, prelink_range->range.offset,
				prelink_range->range.size, R_CF_OPTION_SKIP_NSDATA);
		if (!prelink_info) {
			R_FREE (prelink_range);
			R_FREE (obj);
			goto beach;
		}
	}

	if (!pending_bin_files) {
		pending_bin_files = r_list_new ();
		if (!pending_bin_files) {
			R_FREE (prelink_range);
			R_FREE (obj);
			R_FREE (prelink_info);
			goto beach;
		}
	}

	obj->mach0 = main_mach0;
	obj->rebase_info = rebase_info;
	obj->prelink_info = prelink_info;
	obj->cache_buf = fbuf;
	obj->pa2va_exec = prelink_range->pa2va_exec;
	obj->pa2va_data = prelink_range->pa2va_data;

	R_FREE (prelink_range);

	*bin_obj = obj;

	r_list_push (pending_bin_files, bf);

	if (rebase_info || main_mach0->chained_starts) {
		RIO *io = bf->rbin->iob.io;
		swizzle_io_read (obj, io);
	}

	return true;

beach:
	r_buf_free (fbuf);
	obj->cache_buf = NULL;
	MACH0_(mach0_free) (main_mach0);
	return false;
}

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


patch 8.2.4359: crash when repeatedly using :retab

Problem:    crash when repeatedly using :retab.
Solution:   Bail out when the line is getting too long.

ex_retab(exarg_T *eap)
{
    linenr_T	lnum;
    int		got_tab = FALSE;
    long	num_spaces = 0;
    long	num_tabs;
    long	len;
    long	col;
    long	vcol;
    long	start_col = 0;		// For start of white-space string
    long	start_vcol = 0;		// For start of white-space string
    long	old_len;
    char_u	*ptr;
    char_u	*new_line = (char_u *)1; // init to non-NULL
    int		did_undo;		// called u_save for current line
#ifdef FEAT_VARTABS
    int		*new_vts_array = NULL;
    char_u	*new_ts_str;		// string value of tab argument
#else
    int		temp;
    int		new_ts;
#endif
    int		save_list;
    linenr_T	first_line = 0;		// first changed line
    linenr_T	last_line = 0;		// last changed line

    save_list = curwin->w_p_list;
    curwin->w_p_list = 0;	    // don't want list mode here

#ifdef FEAT_VARTABS
    new_ts_str = eap->arg;
    if (tabstop_set(eap->arg, &new_vts_array) == FAIL)
	return;
    while (vim_isdigit(*(eap->arg)) || *(eap->arg) == ',')
	++(eap->arg);

    // This ensures that either new_vts_array and new_ts_str are freshly
    // allocated, or new_vts_array points to an existing array and new_ts_str
    // is null.
    if (new_vts_array == NULL)
    {
	new_vts_array = curbuf->b_p_vts_array;
	new_ts_str = NULL;
    }
    else
	new_ts_str = vim_strnsave(new_ts_str, eap->arg - new_ts_str);
#else
    ptr = eap->arg;
    new_ts = getdigits(&ptr);
    if (new_ts < 0 && *eap->arg == '-')
    {
	emsg(_(e_argument_must_be_positive));
	return;
    }
    if (new_ts < 0 || new_ts > TABSTOP_MAX)
    {
	semsg(_(e_invalid_argument_str), eap->arg);
	return;
    }
    if (new_ts == 0)
	new_ts = curbuf->b_p_ts;
#endif
    for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum)
    {
	ptr = ml_get(lnum);
	col = 0;
	vcol = 0;
	did_undo = FALSE;
	for (;;)
	{
	    if (VIM_ISWHITE(ptr[col]))
	    {
		if (!got_tab && num_spaces == 0)
		{
		    // First consecutive white-space
		    start_vcol = vcol;
		    start_col = col;
		}
		if (ptr[col] == ' ')
		    num_spaces++;
		else
		    got_tab = TRUE;
	    }
	    else
	    {
		if (got_tab || (eap->forceit && num_spaces > 1))
		{
		    // Retabulate this string of white-space

		    // len is virtual length of white string
		    len = num_spaces = vcol - start_vcol;
		    num_tabs = 0;
		    if (!curbuf->b_p_et)
		    {
#ifdef FEAT_VARTABS
			int t, s;

			tabstop_fromto(start_vcol, vcol,
					curbuf->b_p_ts, new_vts_array, &t, &s);
			num_tabs = t;
			num_spaces = s;
#else
			temp = new_ts - (start_vcol % new_ts);
			if (num_spaces >= temp)
			{
			    num_spaces -= temp;
			    num_tabs++;
			}
			num_tabs += num_spaces / new_ts;
			num_spaces -= (num_spaces / new_ts) * new_ts;
#endif
		    }
		    if (curbuf->b_p_et || got_tab ||
					(num_spaces + num_tabs < len))
		    {
			if (did_undo == FALSE)
			{
			    did_undo = TRUE;
			    if (u_save((linenr_T)(lnum - 1),
						(linenr_T)(lnum + 1)) == FAIL)
			    {
				new_line = NULL;	// flag out-of-memory
				break;
			    }
			}

			// len is actual number of white characters used
			len = num_spaces + num_tabs;
			old_len = (long)STRLEN(ptr);
			new_line = alloc(old_len - col + start_col + len + 1);
			if (new_line == NULL)
			    break;
			if (start_col > 0)
			    mch_memmove(new_line, ptr, (size_t)start_col);
			mch_memmove(new_line + start_col + len,
				      ptr + col, (size_t)(old_len - col + 1));
			ptr = new_line + start_col;
			for (col = 0; col < len; col++)
			    ptr[col] = (col < num_tabs) ? '\t' : ' ';
			if (ml_replace(lnum, new_line, FALSE) == OK)
			    // "new_line" may have been copied
			    new_line = curbuf->b_ml.ml_line_ptr;
			if (first_line == 0)
			    first_line = lnum;
			last_line = lnum;
			ptr = new_line;
			col = start_col + len;
		    }
		}
		got_tab = FALSE;
		num_spaces = 0;
	    }
	    if (ptr[col] == NUL)
		break;
	    vcol += chartabsize(ptr + col, (colnr_T)vcol);
	    if (has_mbyte)
		col += (*mb_ptr2len)(ptr + col);
	    else
		++col;
	}
	if (new_line == NULL)		    // out of memory
	    break;
	line_breakcheck();
    }
    if (got_int)
	emsg(_(e_interrupted));

#ifdef FEAT_VARTABS
    // If a single value was given then it can be considered equal to
    // either the value of 'tabstop' or the value of 'vartabstop'.
    if (tabstop_count(curbuf->b_p_vts_array) == 0
	&& tabstop_count(new_vts_array) == 1
	&& curbuf->b_p_ts == tabstop_first(new_vts_array))
	; // not changed
    else if (tabstop_count(curbuf->b_p_vts_array) > 0
        && tabstop_eq(curbuf->b_p_vts_array, new_vts_array))
	; // not changed
    else
	redraw_curbuf_later(NOT_VALID);
#else
    if (curbuf->b_p_ts != new_ts)
	redraw_curbuf_later(NOT_VALID);
#endif
    if (first_line != 0)
	changed_lines(first_line, 0, last_line + 1, 0L);

    curwin->w_p_list = save_list;	// restore 'list'

#ifdef FEAT_VARTABS
    if (new_ts_str != NULL)		// set the new tabstop
    {
	// If 'vartabstop' is in use or if the value given to retab has more
	// than one tabstop then update 'vartabstop'.
	int *old_vts_ary = curbuf->b_p_vts_array;

	if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1)
	{
	    set_string_option_direct((char_u *)"vts", -1, new_ts_str,
							OPT_FREE|OPT_LOCAL, 0);
	    curbuf->b_p_vts_array = new_vts_array;
	    vim_free(old_vts_ary);
	}
	else
	{
	    // 'vartabstop' wasn't in use and a single value was given to
	    // retab then update 'tabstop'.
	    curbuf->b_p_ts = tabstop_first(new_vts_array);
	    vim_free(new_vts_array);
	}
	vim_free(new_ts_str);
    }
#else
    curbuf->b_p_ts = new_ts;
#endif
    coladvance(curwin->w_curswant);

    u_clearline();
}

Heap-based Buffer Overflow in GitHub repository vim/vim prior to 8.2.


patch 8.2.5072: using uninitialized value and freed memory in spell command

Problem:    Using uninitialized value and freed memory in spell command.
Solution:   Initialize "attr".  Check for empty line early.

spell_move_to(
    win_T	*wp,
    int		dir,		// FORWARD or BACKWARD
    int		allwords,	// TRUE for "[s"/"]s", FALSE for "[S"/"]S"
    int		curline,
    hlf_T	*attrp)		// return: attributes of bad word or NULL
				// (only when "dir" is FORWARD)
{
    linenr_T	lnum;
    pos_T	found_pos;
    int		found_len = 0;
    char_u	*line;
    char_u	*p;
    char_u	*endp;
    hlf_T	attr;
    int		len;
#ifdef FEAT_SYN_HL
    int		has_syntax = syntax_present(wp);
#endif
    int		col;
    int		can_spell;
    char_u	*buf = NULL;
    int		buflen = 0;
    int		skip = 0;
    int		capcol = -1;
    int		found_one = FALSE;
    int		wrapped = FALSE;

    if (no_spell_checking(wp))
	return 0;

    /*
     * Start looking for bad word at the start of the line, because we can't
     * start halfway a word, we don't know where it starts or ends.
     *
     * When searching backwards, we continue in the line to find the last
     * bad word (in the cursor line: before the cursor).
     *
     * We concatenate the start of the next line, so that wrapped words work
     * (e.g. "et<line-break>cetera").  Doesn't work when searching backwards
     * though...
     */
    lnum = wp->w_cursor.lnum;
    CLEAR_POS(&found_pos);

    while (!got_int)
    {
	line = ml_get_buf(wp->w_buffer, lnum, FALSE);

	len = (int)STRLEN(line);
	if (buflen < len + MAXWLEN + 2)
	{
	    vim_free(buf);
	    buflen = len + MAXWLEN + 2;
	    buf = alloc(buflen);
	    if (buf == NULL)
		break;
	}

	// In first line check first word for Capital.
	if (lnum == 1)
	    capcol = 0;

	// For checking first word with a capital skip white space.
	if (capcol == 0)
	    capcol = getwhitecols(line);
	else if (curline && wp == curwin)
	{
	    // For spellbadword(): check if first word needs a capital.
	    col = getwhitecols(line);
	    if (check_need_cap(lnum, col))
		capcol = col;

	    // Need to get the line again, may have looked at the previous
	    // one.
	    line = ml_get_buf(wp->w_buffer, lnum, FALSE);
	}

	// Copy the line into "buf" and append the start of the next line if
	// possible.
	STRCPY(buf, line);
	if (lnum < wp->w_buffer->b_ml.ml_line_count)
	    spell_cat_line(buf + STRLEN(buf),
			  ml_get_buf(wp->w_buffer, lnum + 1, FALSE), MAXWLEN);

	p = buf + skip;
	endp = buf + len;
	while (p < endp)
	{
	    // When searching backward don't search after the cursor.  Unless
	    // we wrapped around the end of the buffer.
	    if (dir == BACKWARD
		    && lnum == wp->w_cursor.lnum
		    && !wrapped
		    && (colnr_T)(p - buf) >= wp->w_cursor.col)
		break;

	    // start of word
	    attr = HLF_COUNT;
	    len = spell_check(wp, p, &attr, &capcol, FALSE);

	    if (attr != HLF_COUNT)
	    {
		// We found a bad word.  Check the attribute.
		if (allwords || attr == HLF_SPB)
		{
		    // When searching forward only accept a bad word after
		    // the cursor.
		    if (dir == BACKWARD
			    || lnum != wp->w_cursor.lnum
			    || (wrapped
				|| (colnr_T)(curline ? p - buf + len
						     : p - buf)
						  > wp->w_cursor.col))
		    {
#ifdef FEAT_SYN_HL
			if (has_syntax)
			{
			    col = (int)(p - buf);
			    (void)syn_get_id(wp, lnum, (colnr_T)col,
						    FALSE, &can_spell, FALSE);
			    if (!can_spell)
				attr = HLF_COUNT;
			}
			else
#endif
			    can_spell = TRUE;

			if (can_spell)
			{
			    found_one = TRUE;
			    found_pos.lnum = lnum;
			    found_pos.col = (int)(p - buf);
			    found_pos.coladd = 0;
			    if (dir == FORWARD)
			    {
				// No need to search further.
				wp->w_cursor = found_pos;
				vim_free(buf);
				if (attrp != NULL)
				    *attrp = attr;
				return len;
			    }
			    else if (curline)
				// Insert mode completion: put cursor after
				// the bad word.
				found_pos.col += len;
			    found_len = len;
			}
		    }
		    else
			found_one = TRUE;
		}
	    }

	    // advance to character after the word
	    p += len;
	    capcol -= len;
	}

	if (dir == BACKWARD && found_pos.lnum != 0)
	{
	    // Use the last match in the line (before the cursor).
	    wp->w_cursor = found_pos;
	    vim_free(buf);
	    return found_len;
	}

	if (curline)
	    break;	// only check cursor line

	// If we are back at the starting line and searched it again there
	// is no match, give up.
	if (lnum == wp->w_cursor.lnum && wrapped)
	    break;

	// Advance to next line.
	if (dir == BACKWARD)
	{
	    if (lnum > 1)
		--lnum;
	    else if (!p_ws)
		break;	    // at first line and 'nowrapscan'
	    else
	    {
		// Wrap around to the end of the buffer.  May search the
		// starting line again and accept the last match.
		lnum = wp->w_buffer->b_ml.ml_line_count;
		wrapped = TRUE;
		if (!shortmess(SHM_SEARCH))
		    give_warning((char_u *)_(top_bot_msg), TRUE);
	    }
	    capcol = -1;
	}
	else
	{
	    if (lnum < wp->w_buffer->b_ml.ml_line_count)
		++lnum;
	    else if (!p_ws)
		break;	    // at first line and 'nowrapscan'
	    else
	    {
		// Wrap around to the start of the buffer.  May search the
		// starting line again and accept the first match.
		lnum = 1;
		wrapped = TRUE;
		if (!shortmess(SHM_SEARCH))
		    give_warning((char_u *)_(bot_top_msg), TRUE);
	    }

	    // If we are back at the starting line and there is no match then
	    // give up.
	    if (lnum == wp->w_cursor.lnum && !found_one)
		break;

	    // Skip the characters at the start of the next line that were
	    // included in a match crossing line boundaries.
	    if (attr == HLF_COUNT)
		skip = (int)(p - endp);
	    else
		skip = 0;

	    // Capcol skips over the inserted space.
	    --capcol;

	    // But after empty line check first word in next line
	    if (*skipwhite(line) == NUL)
		capcol = 0;
	}

	line_breakcheck();
    }

    vim_free(buf);
    return 0;
}

Use After Free in GitHub repository vim/vim prior to 8.2.


Fix possible stack corruption with XM instrument headers claiming a size of less than 4

Closes #275

mp_sint32 LoaderXM::load(XMFileBase& f, XModule* module)
{
	mp_ubyte insData[230];
	mp_sint32 smpReloc[MP_MAXINSSAMPS];
	mp_ubyte nbu[MP_MAXINSSAMPS];
	mp_uint32 fileSize = 0;

	module->cleanUp();

	// this will make code much easier to read
	TXMHeader*		header = &module->header;
	TXMInstrument*	instr  = module->instr;
	TXMSample*		smp	   = module->smp;
	TXMPattern*		phead  = module->phead;

	// we're already out of memory here
	if (!phead || !instr || !smp)
		return MP_OUT_OF_MEMORY;

	fileSize = f.sizeWithBaseOffset();

	f.read(&header->sig,1,17);
	f.read(&header->name,1,20);
	f.read(&header->whythis1a,1,1);
	header->whythis1a=0;
	f.read(&header->tracker,1,20);
	f.readWords(&header->ver,1);

	if (header->ver != 0x102 &&
		header->ver != 0x103 && // untested
		header->ver != 0x104)
		return MP_LOADER_FAILED;

	f.readDwords(&header->hdrsize,1);

	header->hdrsize-=4;

	mp_uint32 hdrSize = 0x110;
	if (header->hdrsize > hdrSize)
		hdrSize = header->hdrsize;

	mp_ubyte* hdrBuff = new mp_ubyte[hdrSize];
	memset(hdrBuff, 0, hdrSize);

	f.read(hdrBuff, 1, header->hdrsize);

	header->ordnum = LittleEndian::GET_WORD(hdrBuff);
	header->restart = LittleEndian::GET_WORD(hdrBuff+2);
	header->channum = LittleEndian::GET_WORD(hdrBuff+4);
	header->patnum = LittleEndian::GET_WORD(hdrBuff+6);
	header->insnum = LittleEndian::GET_WORD(hdrBuff+8);
	header->freqtab = LittleEndian::GET_WORD(hdrBuff+10);
	header->tempo = LittleEndian::GET_WORD(hdrBuff+12);
	header->speed = LittleEndian::GET_WORD(hdrBuff+14);
	memcpy(header->ord, hdrBuff+16, 256);
	if(header->ordnum > MP_MAXORDERS)
		header->ordnum = MP_MAXORDERS;
	if(header->insnum > MP_MAXINS)
		return MP_LOADER_FAILED;

	delete[] hdrBuff;

	header->mainvol=255;
	header->flags = XModule::MODULE_XMNOTECLIPPING |
		XModule::MODULE_XMARPEGGIO |
		XModule::MODULE_XMPORTANOTEBUFFER |
		XModule::MODULE_XMVOLCOLUMNVIBRATO;

	header->uppernotebound = 119;

	mp_sint32 i,y,sc;
	for (i=0;i<32;i++) header->pan[i]=0x80;

	// old version?
	if (header->ver == 0x102 || header->ver == 0x103)
	{
		mp_sint32 s = 0;
		mp_sint32 e = 0;
		for (y=0;y<header->insnum;y++) {

			f.readDwords(&instr[y].size,1);
			f.read(&instr[y].name,1,22);
			f.read(&instr[y].type,1,1);
			mp_uword numSamples = 0;
			f.readWords(&numSamples,1);
			if(numSamples > MP_MAXINSSAMPS)
				return MP_LOADER_FAILED;
			instr[y].samp = numSamples;

			if (instr[y].size == 29)
			{
#ifdef MILKYTRACKER
				s+=16;
#endif
				for (mp_sint32 i = 0; i < 120; i++)
					instr[y].snum[i] = -1;
				continue;
			}

			f.readDwords(&instr[y].shsize,1);

			memset(insData, 0, 230);

			if (instr[y].size - 33 > 230)
				return MP_OUT_OF_MEMORY;

			f.read(insData, 1, instr[y].size - 33);

			if (instr[y].samp) {
				mp_ubyte* insDataPtr = insData;

				memcpy(nbu, insDataPtr, MP_MAXINSSAMPS);
				insDataPtr+=MP_MAXINSSAMPS;

				TEnvelope venv;
				TEnvelope penv;
				memset(&venv,0,sizeof(venv));
				memset(&penv,0,sizeof(penv));

				mp_sint32 k;
				for (k = 0; k < XM_ENVELOPENUMPOINTS; k++)
				{
					venv.env[k][0] = LittleEndian::GET_WORD(insDataPtr);
					venv.env[k][1] = LittleEndian::GET_WORD(insDataPtr+2);
					insDataPtr+=4;
				}
				for (k = 0; k < XM_ENVELOPENUMPOINTS; k++)
				{
					penv.env[k][0] = LittleEndian::GET_WORD(insDataPtr);
					penv.env[k][1] = LittleEndian::GET_WORD(insDataPtr+2);
					insDataPtr+=4;
				}

				venv.num = *insDataPtr++;
				if (venv.num > XM_ENVELOPENUMPOINTS) venv.num = XM_ENVELOPENUMPOINTS;
				penv.num = *insDataPtr++;
				if (penv.num > XM_ENVELOPENUMPOINTS) penv.num = XM_ENVELOPENUMPOINTS;
				venv.sustain = *insDataPtr++;
				venv.loops = *insDataPtr++;
				venv.loope = *insDataPtr++;
				penv.sustain = *insDataPtr++;
				penv.loops = *insDataPtr++;
				penv.loope = *insDataPtr++;
				venv.type = *insDataPtr++;
				penv.type = *insDataPtr++;

				mp_ubyte vibtype, vibsweep, vibdepth, vibrate;
				mp_uword volfade;

				vibtype = *insDataPtr++;
				vibsweep = *insDataPtr++;
				vibdepth = *insDataPtr++;
				vibrate = *insDataPtr++;

				vibdepth<<=1;

				volfade = LittleEndian::GET_WORD(insDataPtr);
				insDataPtr+=2;
				volfade<<=1;

				//instr[y].res = LittleEndian::GET_WORD(insDataPtr);
				insDataPtr+=2;

				for (mp_sint32 l=0;l<XM_ENVELOPENUMPOINTS;l++) {
					venv.env[l][1]<<=2;
					penv.env[l][1]<<=2;
				}

				if (!module->addVolumeEnvelope(venv))
					return MP_OUT_OF_MEMORY;
				if (!module->addPanningEnvelope(penv))
					return MP_OUT_OF_MEMORY;

				mp_sint32 g=0, sc;
				for (sc=0;sc<instr[y].samp;sc++) {

					smp[g+s].flags=3;
					smp[g+s].venvnum=e+1;
					smp[g+s].penvnum=e+1;

					smp[g+s].vibtype=vibtype;
					smp[g+s].vibsweep=vibsweep;
					smp[g+s].vibdepth=vibdepth;
					smp[g+s].vibrate=vibrate;
					smp[g+s].volfade=volfade;

					// not sure why I did that, actually doesn't make sense
					//if (!(venv.type&1)) smp[g+s].volfade=0;

					f.readDwords(&smp[g+s].samplen,1);
					f.readDwords(&smp[g+s].loopstart,1);
					f.readDwords(&smp[g+s].looplen,1);
					smp[g+s].vol=XModule::vol64to255(f.readByte());
					//f.read(&smp[g+s].vol,1,1);
					f.read(&smp[g+s].finetune,1,1);
					f.read(&smp[g+s].type,1,1);
#ifdef VERBOSE
					printf("Before: %i, After: %i\n", smp[g+s].type, smp[g+s].type & (3+16));
#endif
					f.read(&smp[g+s].pan,1,1);
					f.read(&smp[g+s].relnote,1,1);
					f.read(&smp[g+s].res,1,1);
					f.read(&smp[g+s].name,1,22);

					char line[30];
					memset(line, 0, sizeof(line));
					XModule::convertStr(line, smp[g+s].name, 23, false);
					if (line[0])
						module->addSongMessageLine(line);

					// ignore empty samples
#ifndef MILKYTRACKER
					// ignore empty samples when not being a tracker
					if (smp[g+s].samplen) {
						smpReloc[sc] = g;
						g++;
					}
					else
						smpReloc[sc] = -1;
#else
					smpReloc[sc] = g;
					g++;
#endif
				}

				instr[y].samp = g;

				for (sc = 0; sc < MP_MAXINSSAMPS; sc++) {
					if (smpReloc[nbu[sc]] == -1)
						instr[y].snum[sc] = -1;
					else
						instr[y].snum[sc] = smpReloc[nbu[sc]]+s;
				}

				e++;

			}
			else
			{
				for (mp_sint32 i = 0; i < 120; i++)
					instr[y].snum[i] = -1;
			}

#ifdef MILKYTRACKER
			s+=16;
#else
			s+=instr[y].samp;
#endif

		}

		header->smpnum=s;
		header->volenvnum=e;
		header->panenvnum=e;
	}

	for (y=0;y<header->patnum;y++) {

		if (header->ver == 0x104 || header->ver == 0x103)
		{
			f.readDwords(&phead[y].len,1);
			f.read(&phead[y].ptype,1,1);
			f.readWords(&phead[y].rows,1);
			f.readWords(&phead[y].patdata,1);
		}
		else
		{
			f.readDwords(&phead[y].len,1);
			f.read(&phead[y].ptype,1,1);
			phead[y].rows = (mp_uword)f.readByte()+1;
			f.readWords(&phead[y].patdata,1);
		}

		phead[y].effnum=2;
		phead[y].channum=(mp_ubyte)header->channum;

		phead[y].patternData = new mp_ubyte[phead[y].rows*header->channum*6];

		// out of memory?
		if (phead[y].patternData == NULL)
		{
			return MP_OUT_OF_MEMORY;
		}

		memset(phead[y].patternData,0,phead[y].rows*header->channum*6);

		if (phead[y].patdata) {
			mp_ubyte *buffer = new mp_ubyte[phead[y].patdata];

			// out of memory?
			if (buffer == NULL)
			{
				return MP_OUT_OF_MEMORY;
			}

			f.read(buffer,1,phead[y].patdata);

			//printf("%i\n", phead[y].patdata);

			mp_sint32 pc = 0, bc = 0;
			for (mp_sint32 r=0;r<phead[y].rows;r++) {
				for (mp_sint32 c=0;c<header->channum;c++) {

					mp_ubyte slot[5];
					memset(slot,0,5);

					if ((buffer[pc]&128)) {

						mp_ubyte pb = buffer[pc];
						pc++;

						if ((pb&1)) {
							//phead[y].patternData[bc]=buffer[pc];
							slot[0]=buffer[pc];
							pc++;
						}
						if ((pb&2)) {
							//phead[y].patternData[bc+1]=buffer[pc];
							slot[1]=buffer[pc];
							pc++;
						}
						if ((pb&4)) {
							//phead[y].patternData[bc+2]=buffer[pc];
							slot[2]=buffer[pc];
							pc++;
						}
						if ((pb&8)) {
							//phead[y].patternData[bc+3]=buffer[pc];
							slot[3]=buffer[pc];
							pc++;
						}
						if ((pb&16)) {
							//phead[y].patternData[bc+4]=buffer[pc];
							slot[4]=buffer[pc];
							pc++;
						}

					}
					else {
						//memcpy(phead[y].patternData+bc,buffer+pc,5);
						memcpy(slot,buffer+pc,5);
						pc+=5;
					}

					char gl=0;
					for (mp_sint32 i=0;i<XModule::numValidXMEffects;i++)
						if (slot[3]==XModule::validXMEffects[i]) gl=1;

					if (!gl) slot[3]=slot[4]=0;

					if ((slot[3]==0xC)||(slot[3]==0x10)) {
						slot[4] = XModule::vol64to255(slot[4]);
						/*mp_sint32 bl = slot[4];
						if (bl>64) bl=64;
						slot[4]=(bl*261120)>>16;*/
					}

					if ((!slot[3])&&(slot[4])) slot[3]=0x20;

					if (slot[3]==0xE) {
						slot[3]=(slot[4]>>4)+0x30;
						slot[4]=slot[4]&0xf;
					}

					if (slot[3]==0x21) {
						slot[3]=(slot[4]>>4)+0x40;
						slot[4]=slot[4]&0xf;
					}

					if (slot[0]==97) slot[0]=XModule::NOTE_OFF;

					phead[y].patternData[bc]=slot[0];
					phead[y].patternData[bc+1]=slot[1];

					XModule::convertXMVolumeEffects(slot[2], phead[y].patternData[bc+2], phead[y].patternData[bc+3]);

					phead[y].patternData[bc+4]=slot[3];
					phead[y].patternData[bc+5]=slot[4];

					/*if ((y==3)&&(c==2)) {
						for (mp_sint32 bl=0;bl<6;bl++) cprintf("%x ",phead[y].patternData[bc+bl]);
					cprintf("\r\n");
					getch();
					};*/

					/*printf("Note : %i\r\n",phead[y].patternData[bc]);
					printf("Ins  : %i\r\n",phead[y].patternData[bc+1]);
					printf("Vol  : %i\r\n",phead[y].patternData[bc+2]);
					printf("Eff  : %i\r\n",phead[y].patternData[bc+3]);
					printf("Effop: %i\r\n",phead[y].patternData[bc+4]);
					getch();*/

					bc+=6;
				} // for c

			} // for r

			delete[] buffer;
		}

	}

	if (header->ver == 0x104)
	{
		mp_sint32 s = 0;
		mp_sint32 e = 0;
		for (y=0;y<header->insnum;y++) {

			// fixes MOOH.XM loading problems
			// seems to store more instruments in the header than in the actual file
			if (f.posWithBaseOffset() >= fileSize)
				break;

			//TXMInstrument* ins = &instr[y];

			f.readDwords(&instr[y].size,1);

			if (instr[y].size < 29)
			{
				mp_ubyte buffer[29];
				memset(buffer, 0, sizeof(buffer));
				f.read(buffer, 1, instr[y].size - 4);
				memcpy(instr[y].name, buffer, 22);
				instr[y].type = buffer[22];
				instr[y].samp = LittleEndian::GET_WORD(buffer + 23);
			}
			else
			{
				f.read(&instr[y].name,1,22);
				f.read(&instr[y].type,1,1);
				f.readWords(&instr[y].samp,1);
			}
			if (instr[y].samp > MP_MAXINSSAMPS)
				return MP_LOADER_FAILED;

			//printf("%i, %i\n", instr[y].size, instr[y].samp);

			if (instr[y].size <= 29)
			{
#ifdef MILKYTRACKER
				s+=16;
#endif
				for (mp_sint32 i = 0; i < 120; i++)
					instr[y].snum[i] = -1;
				continue;
			}

			f.readDwords(&instr[y].shsize,1);
#ifdef VERBOSE
			printf("%i/%i: %i, %i, %i, %s\n",y,header->insnum-1,instr[y].size,instr[y].shsize,instr[y].samp,instr[y].name);
#endif
			memset(insData, 0, 230);

			if (instr[y].size - 33 > 230)
			{
				//return -7;
				break;
			}

			f.read(insData, 1, instr[y].size - 33);

			/*printf("%i\r\n",instr[y].size);
			printf("%s\r\n",instr[y].name);
			printf("%i\r\n",instr[y].type);
			printf("%i\r\n",instr[y].samp);
			printf("%i\r\n",instr[y].shsize);*/
			//getch();

			memset(smpReloc, 0, sizeof(smpReloc));

			if (instr[y].samp) {
				mp_ubyte* insDataPtr = insData;

				//f.read(&nbu,1,96);

				memcpy(nbu, insDataPtr, MP_MAXINSSAMPS);
				insDataPtr+=MP_MAXINSSAMPS;

				TEnvelope venv;
				TEnvelope penv;
				memset(&venv,0,sizeof(venv));
				memset(&penv,0,sizeof(penv));

				mp_sint32 k;
				for (k = 0; k < XM_ENVELOPENUMPOINTS; k++)
				{
					venv.env[k][0] = LittleEndian::GET_WORD(insDataPtr);
					venv.env[k][1] = LittleEndian::GET_WORD(insDataPtr+2);
					insDataPtr+=4;
				}
				for (k = 0; k < XM_ENVELOPENUMPOINTS; k++)
				{
					penv.env[k][0] = LittleEndian::GET_WORD(insDataPtr);
					penv.env[k][1] = LittleEndian::GET_WORD(insDataPtr+2);
					insDataPtr+=4;
				}

				venv.num = *insDataPtr++;
				if (venv.num > XM_ENVELOPENUMPOINTS) venv.num = XM_ENVELOPENUMPOINTS;
				penv.num = *insDataPtr++;
				if (penv.num > XM_ENVELOPENUMPOINTS) penv.num = XM_ENVELOPENUMPOINTS;
				venv.sustain = *insDataPtr++;
				venv.loops = *insDataPtr++;
				venv.loope = *insDataPtr++;
				penv.sustain = *insDataPtr++;
				penv.loops = *insDataPtr++;
				penv.loope = *insDataPtr++;
				venv.type = *insDataPtr++;
				penv.type = *insDataPtr++;

				mp_ubyte vibtype, vibsweep, vibdepth, vibrate;
				mp_uword volfade;

				vibtype = *insDataPtr++;
				vibsweep = *insDataPtr++;
				vibdepth = *insDataPtr++;
				vibrate = *insDataPtr++;

				vibdepth<<=1;

				//f.readWords(&volfade,1);
				volfade = LittleEndian::GET_WORD(insDataPtr);
				insDataPtr+=2;
				volfade<<=1;

				//instr[y].res = LittleEndian::GET_WORD(insDataPtr);
				insDataPtr+=2;

				for (mp_sint32 l=0;l<XM_ENVELOPENUMPOINTS;l++) {
					venv.env[l][1]<<=2;
					penv.env[l][1]<<=2;
				}

				if (!module->addVolumeEnvelope(venv))
					return MP_OUT_OF_MEMORY;
				if (!module->addPanningEnvelope(penv))
					return MP_OUT_OF_MEMORY;

				mp_sint32 g=0, sc;
				for (sc=0;sc<instr[y].samp;sc++) {
					//TXMSample* smpl = &smp[g+s];

					smp[g+s].flags=3;
					smp[g+s].venvnum=e+1;
					smp[g+s].penvnum=e+1;

					smp[g+s].vibtype=vibtype;
					smp[g+s].vibsweep=vibsweep;
					smp[g+s].vibdepth=vibdepth;
					smp[g+s].vibrate=vibrate;
					smp[g+s].volfade=volfade;

					// not sure why I did that, actually doesn't make sense
					//if (!(venv.type&1)) smp[g+s].volfade=0;

					f.readDwords(&smp[g+s].samplen,1);

					f.readDwords(&smp[g+s].loopstart,1);
					f.readDwords(&smp[g+s].looplen,1);
					smp[g+s].vol=XModule::vol64to255(f.readByte());
					//f.read(&smp[g+s].vol,1,1);
					f.read(&smp[g+s].finetune,1,1);
					f.read(&smp[g+s].type,1,1);
#ifdef VERBOSE
					printf("Before: %i, After: %i\n", smp[g+s].type, smp[g+s].type & (3+16));
#endif
					f.read(&smp[g+s].pan,1,1);
					f.read(&smp[g+s].relnote,1,1);
					f.read(&smp[g+s].res,1,1);
					f.read(&smp[g+s].name,1,22);

					char line[30];
					memset(line, 0, sizeof(line));
					XModule::convertStr(line, smp[g+s].name, 23, false);
					if (line[0])
						module->addSongMessageLine(line);

#ifndef MILKYTRACKER
					// ignore empty samples when not being a tracker
					if (smp[g+s].samplen) {
						smpReloc[sc] = g;
						g++;
					}
					else
						smpReloc[sc] = -1;
#else
					smpReloc[sc] = g;
					g++;
#endif
				}

				instr[y].samp = g;

				for (sc = 0; sc < MP_MAXINSSAMPS; sc++) {
					if (smpReloc[nbu[sc]] == -1)
						instr[y].snum[sc] = -1;
					else
						instr[y].snum[sc] = smpReloc[nbu[sc]]+s;
				}

				for (sc=0;sc<instr[y].samp;sc++) {

					if (smp[s].samplen)
					{
						bool adpcm = (smp[s].res == 0xAD);

						mp_uint32 oldSize = smp[s].samplen;
						if (smp[s].type&16)
						{
							smp[s].samplen>>=1;
							smp[s].loopstart>>=1;
							smp[s].looplen>>=1;
						}

						mp_sint32 result = module->loadModuleSample(f, s,
													 adpcm ? XModule::ST_PACKING_ADPCM : XModule::ST_DELTA,
													 adpcm ? (XModule::ST_PACKING_ADPCM | XModule::ST_16BIT) : (XModule::ST_DELTA | XModule::ST_16BIT),
													 oldSize);
						if (result != MP_OK)
							return result;

						if (adpcm)
							smp[s].res = 0;
					}

					s++;

					if (s>=MP_MAXSAMPLES)
						return MP_OUT_OF_MEMORY;

				}

				e++;

			}
			else
			{
				for (mp_sint32 i = 0; i < 120; i++)
					instr[y].snum[i] = -1;
			}

#ifdef MILKYTRACKER
			s+=16 - instr[y].samp;
#endif

		}

		header->smpnum=s;
		header->volenvnum=e;
		header->panenvnum=e;

	}
	else
	{
		mp_sint32 s = 0;
		for (y=0;y<header->insnum;y++) {
			for (sc=0;sc<instr[y].samp;sc++) {

				if (smp[s].samplen)
				{
					mp_uint32 oldSize = smp[s].samplen;
					if (smp[s].type&16)
					{
						smp[s].samplen>>=1;
						smp[s].loopstart>>=1;
						smp[s].looplen>>=1;
					}

					mp_sint32 result = module->loadModuleSample(f, s, XModule::ST_DELTA, XModule::ST_DELTA | XModule::ST_16BIT, oldSize);
					if (result != MP_OK)
						return result;
				}

				s++;

				if (s>=MP_MAXSAMPLES)
					return MP_OUT_OF_MEMORY;
			}

#ifdef MILKYTRACKER
			s+=16 - instr[y].samp;
#endif

		}
	}

	// convert modplug stereo samples
	for (mp_sint32 s = 0; s < header->smpnum; s++)
	{
		if (smp[s].type & 32)
		{
			// that's what's allowed, stupid modplug tracker
			smp[s].type &= 3+16;

			if (smp[s].sample == NULL)
				continue;

			if (!(smp[s].type&16)) {
				smp[s].samplen>>=1;
				smp[s].loopstart>>=1;
				smp[s].looplen>>=1;

				mp_sbyte* sample = (mp_sbyte*)smp[s].sample;
				mp_sint32 samplen = smp[s].samplen;
				for (mp_sint32 i = 0; i < samplen; i++)
				{
					mp_sint32 s = ((mp_sint32)sample[i] + (mp_sint32)sample[i + samplen]) >> 1;
					if (s < -128) s = -128;
					if (s > 127) s = 127;
					sample[i] = (mp_sbyte)s;
				}
			}
			else
			{
				smp[s].samplen>>=1;
				smp[s].loopstart>>=1;
				smp[s].looplen>>=1;

				mp_sword* sample = (mp_sword*)smp[s].sample;
				mp_sint32 samplen = smp[s].samplen;
				for (mp_sint32 i = 0; i < samplen; i++)
				{
					mp_sint32 s = ((mp_sint32)sample[i] + (mp_sint32)sample[i + samplen]) >> 1;
					if (s < -32768) s = -32768;
					if (s > 32767) s = 32767;
					sample[i] = (mp_sword)s;
				}
			}
		}

		// correct loop type 0x03 (undefined)
		// will become ping pong loop
		// note that FT2 will refuse to load XM files with such a loop type
		if ((smp[s].type & 0x3) == 0x3)
			smp[s].type&=~1;
	}

	// correct number of patterns if necessary, otherwise the post processing will remove
	// the "invalid" patterns from the order list
	bool addPatterns = false;
	for (i = 0; i < header->ordnum; i++)
		if (header->ord[i]+1 > header->patnum)
		{
			header->patnum = header->ord[i]+1;
			addPatterns = true;
		}

	// if the pattern number has been adjusted, add some empty patterns
	if (addPatterns)
	{
		for (i = 0; i < header->patnum; i++)
			if (phead[i].patternData == NULL)
			{
				phead[i].rows = 64;
				phead[i].effnum = 2;
				phead[i].channum = (mp_ubyte)header->channum;

				phead[i].patternData = new mp_ubyte[phead[i].rows*header->channum*6];

				// out of memory?
				if (phead[i].patternData == NULL)
				{
					return MP_OUT_OF_MEMORY;
				}

				memset(phead[i].patternData,0,phead[i].rows*header->channum*6);
			}
	}

	// check for MODPLUG extensions
	if (f.posWithBaseOffset() + 8 <= fileSize)
	{
		char buffer[4];
		f.read(buffer, 1, 4);
		if (memcmp(buffer, "text", 4) == 0)
		{
			mp_uint32 len = f.readDword();
			module->allocateSongMessage(len+1);

			memset(module->message, 0, len+1);

			f.read(module->message, 1, len);
		}
	}

	module->postProcessSamples();

	return MP_OK;
}

MilkyTracker v1.03.00 was discovered to contain a stack overflow via the component LoaderXM::load. This vulnerability is triggered when the program is supplied a crafted XM module file.