<commit_before>/*
 *  request.c
 *
 *  Copyright (C) 2001 by <NAME>
 *
 *  Please add a note about your changes to smbfs in the ChangeLog file.
 */

#include <linux/types.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/net.h>

#include <linux/smb_fs.h>
#include <linux/smbno.h>
#include <linux/smb_mount.h>

#include "smb_debug.h"
#include "request.h"
#include "proto.h"

/* #define SMB_SLAB_DEBUG	(SLAB_RED_ZONE | SLAB_POISON) */
#define SMB_SLAB_DEBUG	0

#define ROUND_UP(x) (((x)+3) & ~3)

/* cache for request structures */
static kmem_cache_t *req_cachep;

static int smb_request_send_req(struct smb_request *req);

/*
  /proc/slabinfo:
  name, active, num, objsize, active_slabs, num_slaps, #pages
*/


int smb_init_request_cache(void)
{
	req_cachep = kmem_cache_create("smb_request",
				       sizeof(struct smb_request), 0,
				       SMB_SLAB_DEBUG | SLAB_HWCACHE_ALIGN,
				       NULL, NULL);
	if (req_cachep == NULL)
		return -ENOMEM;

	return 0;
}

void smb_destroy_request_cache(void)
{
	if (kmem_cache_destroy(req_cachep))
		printk(KERN_INFO "smb_destroy_request_cache: not all structures were freed\n");
}

/*
 * Allocate and initialise a request structure
 */
static struct smb_request *smb_do_alloc_request(struct smb_sb_info *server,
						int bufsize)
{
	struct smb_request *req;
	unsigned char *buf = NULL;

	req = kmem_cache_alloc(req_cachep, SLAB_KERNEL);
	VERBOSE("allocating request: %p\n", req);
	if (!req)
		goto out;

	if (bufsize > 0) {
		buf = kmalloc(bufsize, GFP_NOFS);
		if (!buf) {
			kmem_cache_free(req_cachep, req);
			return NULL;
		}
	}

	memset(req, 0, sizeof(struct smb_request));
	req->rq_buffer = buf;
	req->rq_bufsize = bufsize;
	req->rq_server = server;
	init_waitqueue_head(&req->rq_wait);
	INIT_LIST_HEAD(&req->rq_queue);
	atomic_set(&req->rq_count, 1);

out:
	return req;
}

struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize)
{
	struct smb_request *req = NULL;

	for (;;) {
		atomic_inc(&server->nr_requests);
		if (atomic_read(&server->nr_requests) <= MAX_REQUEST_HARD) {
			req = smb_do_alloc_request(server, bufsize);
			if (req != NULL)
				break;
		}

#if 0
		/*
		 * Try to free up at least one request in order to stay
		 * below the hard limit
		 */
                if (nfs_try_to_free_pages(server))
			continue;

		if (signalled() && (server->flags & NFS_MOUNT_INTR))
			return ERR_PTR(-ERESTARTSYS);
		current->policy = SCHED_YIELD;
		schedule();
#else
		/* FIXME: we want something like nfs does above, but that
		   requires changes to all callers and can wait. */
		break;
#endif
	}
	return req;
}

static void smb_free_request(struct smb_request *req)
{
	atomic_dec(&req->rq_server->nr_requests);
	if (req->rq_buffer && !(req->rq_flags & SMB_REQ_STATIC))
		kfree(req->rq_buffer);
	kfree(req->rq_trans2buffer);
	kmem_cache_free(req_cachep, req);
}

/*
 * What prevents a rget to race with a rput? The count must never drop to zero
 * while it is in use. Only rput if it is ok that it is free'd.
 */
static void smb_rget(struct smb_request *req)
{
	atomic_inc(&req->rq_count);
}
void smb_rput(struct smb_request *req)
{
	if (atomic_dec_and_test(&req->rq_count)) {
		list_del_init(&req->rq_queue);
		smb_free_request(req);
	}
}

/* setup to receive the data part of the SMB */
static int smb_setup_bcc(struct smb_request *req)
{
	int result = 0;
	req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd;

	if (req->rq_rlen > req->rq_bufsize) {
		PARANOIA("Packet too large %d > %d\n",
			 req->rq_rlen, req->rq_bufsize);
		return -ENOBUFS;
	}

	req->rq_iov[0].iov_base = req->rq_buffer;
	req->rq_iov[0].iov_len  = req->rq_rlen;
	req->rq_iovlen = 1;

	return result;
}

/*
 * Prepare a "normal" request structure.
 */
static int smb_setup_request(struct smb_request *req)
{
	int len = smb_len(req->rq_header) + 4;
	req->rq_slen = len;

	/* if we expect a data part in the reply we set the iov's to read it */
	if (req->rq_resp_bcc)
		req->rq_setup_read = smb_setup_bcc;

	/* This tries to support re-using the same request */
	req->rq_bytes_sent = 0;
	req->rq_rcls = 0;
	req->rq_err = 0;
	req->rq_errno = 0;
	req->rq_fragment = 0;
	kfree(req->rq_trans2buffer);

	return 0;
}

/*
 * Prepare a transaction2 request structure
 */
static int smb_setup_trans2request(struct smb_request *req)
{
	struct smb_sb_info *server = req->rq_server;
	int mparam, mdata;
	static unsigned char padding[4];

	/* I know the following is very ugly, but I want to build the
	   smb packet as efficiently as possible. */

	const int smb_parameters = 15;
	const int header = SMB_HEADER_LEN + 2 * smb_parameters + 2;
	const int oparam = ROUND_UP(header + 3);
	const int odata  = ROUND_UP(oparam + req->rq_lparm);
	const int bcc = (req->rq_data ? odata + req->rq_ldata :
					oparam + req->rq_lparm) - header;

	if ((bcc + oparam) > server->opt.max_xmit)
		return -ENOMEM;
	smb_setup_header(req, SMBtrans2, smb_parameters, bcc);

	/*
	 * max parameters + max data + max setup == bufsize to make NT4 happy
	 * and not abort the transfer or split into multiple responses. It also
	 * makes smbfs happy as handling packets larger than the buffer size
	 * is extra work.
	 *
	 * OS/2 is probably going to hate me for this ...
	 */
	mparam = SMB_TRANS2_MAX_PARAM;
	mdata = req->rq_bufsize - mparam;

	mdata = server->opt.max_xmit - mparam - 100;
	if (mdata < 1024) {
		mdata = 1024;
		mparam = 20;
	}

#if 0
	/* NT/win2k has ~4k max_xmit, so with this we request more than it wants
	   to return as one SMB. Useful for testing the fragmented trans2
	   handling. */
	mdata = 8192;
#endif

	WSET(req->rq_header, smb_tpscnt, req->rq_lparm);
	WSET(req->rq_header, smb_tdscnt, req->rq_ldata);
	WSET(req->rq_header, smb_mprcnt, mparam);
	WSET(req->rq_header, smb_mdrcnt, mdata);
	WSET(req->rq_header, smb_msrcnt, 0);    /* max setup always 0 ? */
	WSET(req->rq_header, smb_flags, 0);
	DSET(req->rq_header, smb_timeout, 0);
	WSET(req->rq_header, smb_pscnt, req->rq_lparm);
	WSET(req->rq_header, smb_psoff, oparam - 4);
	WSET(req->rq_header, smb_dscnt, req->rq_ldata);
	WSET(req->rq_header, smb_dsoff, req->rq_data ? odata - 4 : 0);
	*(req->rq_header + smb_suwcnt) = 0x01;          /* setup count */
	*(req->rq_header + smb_suwcnt + 1) = 0x00;      /* reserved */
	WSET(req->rq_header, smb_setup0, req->rq_trans2_command);

	req->rq_iovlen = 2;
	req->rq_iov[0].iov_base = (void *) req->rq_header;
	req->rq_iov[0].iov_len = oparam;
	req->rq_iov[1].iov_base = (req->rq_parm==NULL) ? padding : req->rq_parm;
	req->rq_iov[1].iov_len = req->rq_lparm;
	req->rq_slen = oparam + req->rq_lparm;

	if (req->rq_data) {
		req->rq_iovlen += 2;
		req->rq_iov[2].iov_base = padding;
		req->rq_iov[2].iov_len = odata - oparam - req->rq_lparm;
		req->rq_iov[3].iov_base = req->rq_data;
		req->rq_iov[3].iov_len = req->rq_ldata;
		req->rq_slen = odata + req->rq_ldata;
	}

	/* always a data part for trans2 replies */
	req->rq_setup_read = smb_setup_bcc;

	return 0;
}

/*
 * Add a request and tell smbiod to process it
 */
int smb_add_request(struct smb_request *req)
{
	long timeleft;
	struct smb_sb_info *server = req->rq_server;
	int result = 0;

	smb_setup_request(req);
	if (req->rq_trans2_command) {
		if (req->rq_buffer == NULL) {
			PARANOIA("trans2 attempted without response buffer!\n");
			return -EIO;
		}
		result = smb_setup_trans2request(req);
	}
	if (result < 0)
		return result;

#ifdef SMB_DEBUG_PACKET_SIZE
	add_xmit_stats(req);
#endif

	/* add 'req' to the queue of requests */
	if (smb_lock_server_interruptible(server))
		return -EINTR;

	/*
	 * Try to send the request as the process. If that fails we queue the
	 * request and let smbiod send it later.
	 */

	/* FIXME: each server has a number on the maximum number of parallel
	   requests. 10, 50 or so. We should not allow more requests to be
	   active. */
	if (server->mid > 0xf000)
		server->mid = 0;
	req->rq_mid = server->mid++;
	WSET(req->rq_header, smb_mid, req->rq_mid);

	result = 0;
	if (server->state == CONN_VALID) {
		if (list_empty(&server->xmitq))
			result = smb_request_send_req(req);
		if (result < 0) {
			/* Connection lost? */
			server->conn_error = result;
			server->state = CONN_INVALID;
		}
	}
	if (result != 1)
		list_add_tail(&req->rq_queue, &server->xmitq);
	smb_rget(req);

	if (server->state != CONN_VALID)
		smbiod_retry(server);

	smb_unlock_server(server);

	smbiod_wake_up();

	timeleft = wait_event_interruptible_timeout(req->rq_wait,
				    req->rq_flags & SMB_REQ_RECEIVED, 30*HZ);
	if (!timeleft || signal_pending(current)) {
		/*
		 * On timeout or on interrupt we want to try and remove the
		 * request from the recvq/xmitq.
		 */
		smb_lock_server(server);
		if (!(req->rq_flags & SMB_REQ_RECEIVED)) {
			list_del_init(&req->rq_queue);
			smb_rput(req);<commit_msg>[PATCH] smbfs: Fix slab corruption in samba error path<commit_after>/*
 *  request.c
 *
 *  Copyright (C) 2001 by <NAME>
 *
 *  Please add a note about your changes to smbfs in the ChangeLog file.
 */

#include <linux/types.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/net.h>

#include <linux/smb_fs.h>
#include <linux/smbno.h>
#include <linux/smb_mount.h>

#include "smb_debug.h"
#include "request.h"
#include "proto.h"

/* #define SMB_SLAB_DEBUG	(SLAB_RED_ZONE | SLAB_POISON) */
#define SMB_SLAB_DEBUG	0

#define ROUND_UP(x) (((x)+3) & ~3)

/* cache for request structures */
static kmem_cache_t *req_cachep;

static int smb_request_send_req(struct smb_request *req);

/*
  /proc/slabinfo:
  name, active, num, objsize, active_slabs, num_slaps, #pages
*/


int smb_init_request_cache(void)
{
	req_cachep = kmem_cache_create("smb_request",
				       sizeof(struct smb_request), 0,
				       SMB_SLAB_DEBUG | SLAB_HWCACHE_ALIGN,
				       NULL, NULL);
	if (req_cachep == NULL)
		return -ENOMEM;

	return 0;
}

void smb_destroy_request_cache(void)
{
	if (kmem_cache_destroy(req_cachep))
		printk(KERN_INFO "smb_destroy_request_cache: not all structures were freed\n");
}

/*
 * Allocate and initialise a request structure
 */
static struct smb_request *smb_do_alloc_request(struct smb_sb_info *server,
						int bufsize)
{
	struct smb_request *req;
	unsigned char *buf = NULL;

	req = kmem_cache_alloc(req_cachep, SLAB_KERNEL);
	VERBOSE("allocating request: %p\n", req);
	if (!req)
		goto out;

	if (bufsize > 0) {
		buf = kmalloc(bufsize, GFP_NOFS);
		if (!buf) {
			kmem_cache_free(req_cachep, req);
			return NULL;
		}
	}

	memset(req, 0, sizeof(struct smb_request));
	req->rq_buffer = buf;
	req->rq_bufsize = bufsize;
	req->rq_server = server;
	init_waitqueue_head(&req->rq_wait);
	INIT_LIST_HEAD(&req->rq_queue);
	atomic_set(&req->rq_count, 1);

out:
	return req;
}

struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize)
{
	struct smb_request *req = NULL;

	for (;;) {
		atomic_inc(&server->nr_requests);
		if (atomic_read(&server->nr_requests) <= MAX_REQUEST_HARD) {
			req = smb_do_alloc_request(server, bufsize);
			if (req != NULL)
				break;
		}

#if 0
		/*
		 * Try to free up at least one request in order to stay
		 * below the hard limit
		 */
                if (nfs_try_to_free_pages(server))
			continue;

		if (signalled() && (server->flags & NFS_MOUNT_INTR))
			return ERR_PTR(-ERESTARTSYS);
		current->policy = SCHED_YIELD;
		schedule();
#else
		/* FIXME: we want something like nfs does above, but that
		   requires changes to all callers and can wait. */
		break;
#endif
	}
	return req;
}

static void smb_free_request(struct smb_request *req)
{
	atomic_dec(&req->rq_server->nr_requests);
	if (req->rq_buffer && !(req->rq_flags & SMB_REQ_STATIC))
		kfree(req->rq_buffer);
	kfree(req->rq_trans2buffer);
	kmem_cache_free(req_cachep, req);
}

/*
 * What prevents a rget to race with a rput? The count must never drop to zero
 * while it is in use. Only rput if it is ok that it is free'd.
 */
static void smb_rget(struct smb_request *req)
{
	atomic_inc(&req->rq_count);
}
void smb_rput(struct smb_request *req)
{
	if (atomic_dec_and_test(&req->rq_count)) {
		list_del_init(&req->rq_queue);
		smb_free_request(req);
	}
}

/* setup to receive the data part of the SMB */
static int smb_setup_bcc(struct smb_request *req)
{
	int result = 0;
	req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd;

	if (req->rq_rlen > req->rq_bufsize) {
		PARANOIA("Packet too large %d > %d\n",
			 req->rq_rlen, req->rq_bufsize);
		return -ENOBUFS;
	}

	req->rq_iov[0].iov_base = req->rq_buffer;
	req->rq_iov[0].iov_len  = req->rq_rlen;
	req->rq_iovlen = 1;

	return result;
}

/*
 * Prepare a "normal" request structure.
 */
static int smb_setup_request(struct smb_request *req)
{
	int len = smb_len(req->rq_header) + 4;
	req->rq_slen = len;

	/* if we expect a data part in the reply we set the iov's to read it */
	if (req->rq_resp_bcc)
		req->rq_setup_read = smb_setup_bcc;

	/* This tries to support re-using the same request */
	req->rq_bytes_sent = 0;
	req->rq_rcls = 0;
	req->rq_err = 0;
	req->rq_errno = 0;
	req->rq_fragment = 0;
	kfree(req->rq_trans2buffer);

	return 0;
}

/*
 * Prepare a transaction2 request structure
 */
static int smb_setup_trans2request(struct smb_request *req)
{
	struct smb_sb_info *server = req->rq_server;
	int mparam, mdata;
	static unsigned char padding[4];

	/* I know the following is very ugly, but I want to build the
	   smb packet as efficiently as possible. */

	const int smb_parameters = 15;
	const int header = SMB_HEADER_LEN + 2 * smb_parameters + 2;
	const int oparam = ROUND_UP(header + 3);
	const int odata  = ROUND_UP(oparam + req->rq_lparm);
	const int bcc = (req->rq_data ? odata + req->rq_ldata :
					oparam + req->rq_lparm) - header;

	if ((bcc + oparam) > server->opt.max_xmit)
		return -ENOMEM;
	smb_setup_header(req, SMBtrans2, smb_parameters, bcc);

	/*
	 * max parameters + max data + max setup == bufsize to make NT4 happy
	 * and not abort the transfer or split into multiple responses. It also
	 * makes smbfs happy as handling packets larger than the buffer size
	 * is extra work.
	 *
	 * OS/2 is probably going to hate me for this ...
	 */
	mparam = SMB_TRANS2_MAX_PARAM;
	mdata = req->rq_bufsize - mparam;

	mdata = server->opt.max_xmit - mparam - 100;
	if (mdata < 1024) {
		mdata = 1024;
		mparam = 20;
	}

#if 0
	/* NT/win2k has ~4k max_xmit, so with this we request more than it wants
	   to return as one SMB. Useful for testing the fragmented trans2
	   handling. */
	mdata = 8192;
#endif

	WSET(req->rq_header, smb_tpscnt, req->rq_lparm);
	WSET(req->rq_header, smb_tdscnt, req->rq_ldata);
	WSET(req->rq_header, smb_mprcnt, mparam);
	WSET(req->rq_header, smb_mdrcnt, mdata);
	WSET(req->rq_header, smb_msrcnt, 0);    /* max setup always 0 ? */
	WSET(req->rq_header, smb_flags, 0);
	DSET(req->rq_header, smb_timeout, 0);
	WSET(req->rq_header, smb_pscnt, req->rq_lparm);
	WSET(req->rq_header, smb_psoff, oparam - 4);
	WSET(req->rq_header, smb_dscnt, req->rq_ldata);
	WSET(req->rq_header, smb_dsoff, req->rq_data ? odata - 4 : 0);
	*(req->rq_header + smb_suwcnt) = 0x01;          /* setup count */
	*(req->rq_header + smb_suwcnt + 1) = 0x00;      /* reserved */
	WSET(req->rq_header, smb_setup0, req->rq_trans2_command);

	req->rq_iovlen = 2;
	req->rq_iov[0].iov_base = (void *) req->rq_header;
	req->rq_iov[0].iov_len = oparam;
	req->rq_iov[1].iov_base = (req->rq_parm==NULL) ? padding : req->rq_parm;
	req->rq_iov[1].iov_len = req->rq_lparm;
	req->rq_slen = oparam + req->rq_lparm;

	if (req->rq_data) {
		req->rq_iovlen += 2;
		req->rq_iov[2].iov_base = padding;
		req->rq_iov[2].iov_len = odata - oparam - req->rq_lparm;
		req->rq_iov[3].iov_base = req->rq_data;
		req->rq_iov[3].iov_len = req->rq_ldata;
		req->rq_slen = odata + req->rq_ldata;
	}

	/* always a data part for trans2 replies */
	req->rq_setup_read = smb_setup_bcc;

	return 0;
}

/*
 * Add a request and tell smbiod to process it
 */
int smb_add_request(struct smb_request *req)
{
	long timeleft;
	struct smb_sb_info *server = req->rq_server;
	int result = 0;

	smb_setup_request(req);
	if (req->rq_trans2_command) {
		if (req->rq_buffer == NULL) {
			PARANOIA("trans2 attempted without response buffer!\n");
			return -EIO;
		}
		result = smb_setup_trans2request(req);
	}
	if (result < 0)
		return result;

#ifdef SMB_DEBUG_PACKET_SIZE
	add_xmit_stats(req);
#endif

	/* add 'req' to the queue of requests */
	if (smb_lock_server_interruptible(server))
		return -EINTR;

	/*
	 * Try to send the request as the process. If that fails we queue the
	 * request and let smbiod send it later.
	 */

	/* FIXME: each server has a number on the maximum number of parallel
	   requests. 10, 50 or so. We should not allow more requests to be
	   active. */
	if (server->mid > 0xf000)
		server->mid = 0;
	req->rq_mid = server->mid++;
	WSET(req->rq_header, smb_mid, req->rq_mid);

	result = 0;
	if (server->state == CONN_VALID) {
		if (list_empty(&server->xmitq))
			result = smb_request_send_req(req);
		if (result < 0) {
			/* Connection lost? */
			server->conn_error = result;
			server->state = CONN_INVALID;
		}
	}
	if (result != 1)
		list_add_tail(&req->rq_queue, &server->xmitq);
	smb_rget(req);

	if (server->state != CONN_VALID)
		smbiod_retry(server);

	smb_unlock_server(server);

	smbiod_wake_up();

	timeleft = wait_event_interruptible_timeout(req->rq_wait,
				    req->rq_flags & SMB_REQ_RECEIVED, 30*HZ);
	if (!timeleft || signal_pending(current)) {
		/*
		 * On timeout or on interrupt we want to try and remove the
		 * request from the recvq/xmitq.
		 * First check if the request is still part of a queue. (May
		 * have been removed by some error condition)
		 */
		smb_lock_server(server);
		if (!list_empty(&req->rq_queue)) {
			list_del_init(&req->rq_queue);
			smb_rput(req);
{'fraction_non_alphanumeric': 0.13300827758249234, 'fraction_numerical': 0.0121328948860415, 'mean_word_length': 3.8445482010436693, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 253, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7176298', 'n_tokens_mistral': 7407, 'n_tokens_neox': 6963, 'n_words': 1701}