2b061f9ef2
We'd like to prevent local buffer overflows caused by malicious or broken servers. New xdr_stream style decoders can do that. For efficiency, we also eventually want to be able to pass xdr_streams from call_encode() and call_decode() to all XDR encoding functions, rather than building an xdr_stream in every XDR encoding and decoding function in the kernel. To do all of this, rewrite the XDR encoding and decoding functions in fs/lockd/xdr.c to use xdr_streams. This makes them more or less incompatible with server-side XDR helper functions, so break them out into a separate source file. Static helper functions are left without the "inline" directive. This allows the compiler to choose automatically how to optimize these for size or speed. SHARE-related functionality doesn't seem to be used, as those functions are hiding behind a #define that isn't set anywhere that I can find. And, they've been in there forever (at least as far back as the kernel's git history goes), yet remain unused. Let's take the opportunity to bin them. It should be easy enough for someone to introduce proper XDR functions if at some point SHARE-related NLM functionality is desired. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Tested-by: J. Bruce Fields <bfields@redhat.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
373 lines
7.7 KiB
C
373 lines
7.7 KiB
C
/*
|
|
* linux/fs/lockd/xdr.c
|
|
*
|
|
* XDR support for lockd and the lock client.
|
|
*
|
|
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/nfs.h>
|
|
|
|
#include <linux/sunrpc/xdr.h>
|
|
#include <linux/sunrpc/clnt.h>
|
|
#include <linux/sunrpc/svc.h>
|
|
#include <linux/sunrpc/stats.h>
|
|
#include <linux/lockd/lockd.h>
|
|
|
|
#define NLMDBG_FACILITY NLMDBG_XDR
|
|
|
|
|
|
static inline loff_t
|
|
s32_to_loff_t(__s32 offset)
|
|
{
|
|
return (loff_t)offset;
|
|
}
|
|
|
|
static inline __s32
|
|
loff_t_to_s32(loff_t offset)
|
|
{
|
|
__s32 res;
|
|
if (offset >= NLM_OFFSET_MAX)
|
|
res = NLM_OFFSET_MAX;
|
|
else if (offset <= -NLM_OFFSET_MAX)
|
|
res = -NLM_OFFSET_MAX;
|
|
else
|
|
res = offset;
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* XDR functions for basic NLM types
|
|
*/
|
|
static __be32 *nlm_decode_cookie(__be32 *p, struct nlm_cookie *c)
|
|
{
|
|
unsigned int len;
|
|
|
|
len = ntohl(*p++);
|
|
|
|
if(len==0)
|
|
{
|
|
c->len=4;
|
|
memset(c->data, 0, 4); /* hockeypux brain damage */
|
|
}
|
|
else if(len<=NLM_MAXCOOKIELEN)
|
|
{
|
|
c->len=len;
|
|
memcpy(c->data, p, len);
|
|
p+=XDR_QUADLEN(len);
|
|
}
|
|
else
|
|
{
|
|
dprintk("lockd: bad cookie size %d (only cookies under "
|
|
"%d bytes are supported.)\n",
|
|
len, NLM_MAXCOOKIELEN);
|
|
return NULL;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static inline __be32 *
|
|
nlm_encode_cookie(__be32 *p, struct nlm_cookie *c)
|
|
{
|
|
*p++ = htonl(c->len);
|
|
memcpy(p, c->data, c->len);
|
|
p+=XDR_QUADLEN(c->len);
|
|
return p;
|
|
}
|
|
|
|
static __be32 *
|
|
nlm_decode_fh(__be32 *p, struct nfs_fh *f)
|
|
{
|
|
unsigned int len;
|
|
|
|
if ((len = ntohl(*p++)) != NFS2_FHSIZE) {
|
|
dprintk("lockd: bad fhandle size %d (should be %d)\n",
|
|
len, NFS2_FHSIZE);
|
|
return NULL;
|
|
}
|
|
f->size = NFS2_FHSIZE;
|
|
memset(f->data, 0, sizeof(f->data));
|
|
memcpy(f->data, p, NFS2_FHSIZE);
|
|
return p + XDR_QUADLEN(NFS2_FHSIZE);
|
|
}
|
|
|
|
static inline __be32 *
|
|
nlm_encode_fh(__be32 *p, struct nfs_fh *f)
|
|
{
|
|
*p++ = htonl(NFS2_FHSIZE);
|
|
memcpy(p, f->data, NFS2_FHSIZE);
|
|
return p + XDR_QUADLEN(NFS2_FHSIZE);
|
|
}
|
|
|
|
/*
|
|
* Encode and decode owner handle
|
|
*/
|
|
static inline __be32 *
|
|
nlm_decode_oh(__be32 *p, struct xdr_netobj *oh)
|
|
{
|
|
return xdr_decode_netobj(p, oh);
|
|
}
|
|
|
|
static inline __be32 *
|
|
nlm_encode_oh(__be32 *p, struct xdr_netobj *oh)
|
|
{
|
|
return xdr_encode_netobj(p, oh);
|
|
}
|
|
|
|
static __be32 *
|
|
nlm_decode_lock(__be32 *p, struct nlm_lock *lock)
|
|
{
|
|
struct file_lock *fl = &lock->fl;
|
|
s32 start, len, end;
|
|
|
|
if (!(p = xdr_decode_string_inplace(p, &lock->caller,
|
|
&lock->len,
|
|
NLM_MAXSTRLEN))
|
|
|| !(p = nlm_decode_fh(p, &lock->fh))
|
|
|| !(p = nlm_decode_oh(p, &lock->oh)))
|
|
return NULL;
|
|
lock->svid = ntohl(*p++);
|
|
|
|
locks_init_lock(fl);
|
|
fl->fl_owner = current->files;
|
|
fl->fl_pid = (pid_t)lock->svid;
|
|
fl->fl_flags = FL_POSIX;
|
|
fl->fl_type = F_RDLCK; /* as good as anything else */
|
|
start = ntohl(*p++);
|
|
len = ntohl(*p++);
|
|
end = start + len - 1;
|
|
|
|
fl->fl_start = s32_to_loff_t(start);
|
|
|
|
if (len == 0 || end < 0)
|
|
fl->fl_end = OFFSET_MAX;
|
|
else
|
|
fl->fl_end = s32_to_loff_t(end);
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
* Encode result of a TEST/TEST_MSG call
|
|
*/
|
|
static __be32 *
|
|
nlm_encode_testres(__be32 *p, struct nlm_res *resp)
|
|
{
|
|
s32 start, len;
|
|
|
|
if (!(p = nlm_encode_cookie(p, &resp->cookie)))
|
|
return NULL;
|
|
*p++ = resp->status;
|
|
|
|
if (resp->status == nlm_lck_denied) {
|
|
struct file_lock *fl = &resp->lock.fl;
|
|
|
|
*p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
|
|
*p++ = htonl(resp->lock.svid);
|
|
|
|
/* Encode owner handle. */
|
|
if (!(p = xdr_encode_netobj(p, &resp->lock.oh)))
|
|
return NULL;
|
|
|
|
start = loff_t_to_s32(fl->fl_start);
|
|
if (fl->fl_end == OFFSET_MAX)
|
|
len = 0;
|
|
else
|
|
len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
|
|
|
|
*p++ = htonl(start);
|
|
*p++ = htonl(len);
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
/*
|
|
* First, the server side XDR functions
|
|
*/
|
|
int
|
|
nlmsvc_decode_testargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
|
{
|
|
u32 exclusive;
|
|
|
|
if (!(p = nlm_decode_cookie(p, &argp->cookie)))
|
|
return 0;
|
|
|
|
exclusive = ntohl(*p++);
|
|
if (!(p = nlm_decode_lock(p, &argp->lock)))
|
|
return 0;
|
|
if (exclusive)
|
|
argp->lock.fl.fl_type = F_WRLCK;
|
|
|
|
return xdr_argsize_check(rqstp, p);
|
|
}
|
|
|
|
int
|
|
nlmsvc_encode_testres(struct svc_rqst *rqstp, __be32 *p, struct nlm_res *resp)
|
|
{
|
|
if (!(p = nlm_encode_testres(p, resp)))
|
|
return 0;
|
|
return xdr_ressize_check(rqstp, p);
|
|
}
|
|
|
|
int
|
|
nlmsvc_decode_lockargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
|
{
|
|
u32 exclusive;
|
|
|
|
if (!(p = nlm_decode_cookie(p, &argp->cookie)))
|
|
return 0;
|
|
argp->block = ntohl(*p++);
|
|
exclusive = ntohl(*p++);
|
|
if (!(p = nlm_decode_lock(p, &argp->lock)))
|
|
return 0;
|
|
if (exclusive)
|
|
argp->lock.fl.fl_type = F_WRLCK;
|
|
argp->reclaim = ntohl(*p++);
|
|
argp->state = ntohl(*p++);
|
|
argp->monitor = 1; /* monitor client by default */
|
|
|
|
return xdr_argsize_check(rqstp, p);
|
|
}
|
|
|
|
int
|
|
nlmsvc_decode_cancargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
|
{
|
|
u32 exclusive;
|
|
|
|
if (!(p = nlm_decode_cookie(p, &argp->cookie)))
|
|
return 0;
|
|
argp->block = ntohl(*p++);
|
|
exclusive = ntohl(*p++);
|
|
if (!(p = nlm_decode_lock(p, &argp->lock)))
|
|
return 0;
|
|
if (exclusive)
|
|
argp->lock.fl.fl_type = F_WRLCK;
|
|
return xdr_argsize_check(rqstp, p);
|
|
}
|
|
|
|
int
|
|
nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
|
{
|
|
if (!(p = nlm_decode_cookie(p, &argp->cookie))
|
|
|| !(p = nlm_decode_lock(p, &argp->lock)))
|
|
return 0;
|
|
argp->lock.fl.fl_type = F_UNLCK;
|
|
return xdr_argsize_check(rqstp, p);
|
|
}
|
|
|
|
int
|
|
nlmsvc_decode_shareargs(struct svc_rqst *rqstp, __be32 *p, nlm_args *argp)
|
|
{
|
|
struct nlm_lock *lock = &argp->lock;
|
|
|
|
memset(lock, 0, sizeof(*lock));
|
|
locks_init_lock(&lock->fl);
|
|
lock->svid = ~(u32) 0;
|
|
lock->fl.fl_pid = (pid_t)lock->svid;
|
|
|
|
if (!(p = nlm_decode_cookie(p, &argp->cookie))
|
|
|| !(p = xdr_decode_string_inplace(p, &lock->caller,
|
|
&lock->len, NLM_MAXSTRLEN))
|
|
|| !(p = nlm_decode_fh(p, &lock->fh))
|
|
|| !(p = nlm_decode_oh(p, &lock->oh)))
|
|
return 0;
|
|
argp->fsm_mode = ntohl(*p++);
|
|
argp->fsm_access = ntohl(*p++);
|
|
return xdr_argsize_check(rqstp, p);
|
|
}
|
|
|
|
int
|
|
nlmsvc_encode_shareres(struct svc_rqst *rqstp, __be32 *p, struct nlm_res *resp)
|
|
{
|
|
if (!(p = nlm_encode_cookie(p, &resp->cookie)))
|
|
return 0;
|
|
*p++ = resp->status;
|
|
*p++ = xdr_zero; /* sequence argument */
|
|
return xdr_ressize_check(rqstp, p);
|
|
}
|
|
|
|
int
|
|
nlmsvc_encode_res(struct svc_rqst *rqstp, __be32 *p, struct nlm_res *resp)
|
|
{
|
|
if (!(p = nlm_encode_cookie(p, &resp->cookie)))
|
|
return 0;
|
|
*p++ = resp->status;
|
|
return xdr_ressize_check(rqstp, p);
|
|
}
|
|
|
|
int
|
|
nlmsvc_decode_notify(struct svc_rqst *rqstp, __be32 *p, struct nlm_args *argp)
|
|
{
|
|
struct nlm_lock *lock = &argp->lock;
|
|
|
|
if (!(p = xdr_decode_string_inplace(p, &lock->caller,
|
|
&lock->len, NLM_MAXSTRLEN)))
|
|
return 0;
|
|
argp->state = ntohl(*p++);
|
|
return xdr_argsize_check(rqstp, p);
|
|
}
|
|
|
|
int
|
|
nlmsvc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp)
|
|
{
|
|
if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
|
|
return 0;
|
|
argp->state = ntohl(*p++);
|
|
memcpy(&argp->priv.data, p, sizeof(argp->priv.data));
|
|
p += XDR_QUADLEN(SM_PRIV_SIZE);
|
|
return xdr_argsize_check(rqstp, p);
|
|
}
|
|
|
|
int
|
|
nlmsvc_decode_res(struct svc_rqst *rqstp, __be32 *p, struct nlm_res *resp)
|
|
{
|
|
if (!(p = nlm_decode_cookie(p, &resp->cookie)))
|
|
return 0;
|
|
resp->status = *p++;
|
|
return xdr_argsize_check(rqstp, p);
|
|
}
|
|
|
|
int
|
|
nlmsvc_decode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
|
|
{
|
|
return xdr_argsize_check(rqstp, p);
|
|
}
|
|
|
|
int
|
|
nlmsvc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
|
|
{
|
|
return xdr_ressize_check(rqstp, p);
|
|
}
|
|
|
|
#ifdef RPC_DEBUG
|
|
const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
|
|
{
|
|
/*
|
|
* We can get away with a static buffer because we're only
|
|
* called with BKL held.
|
|
*/
|
|
static char buf[2*NLM_MAXCOOKIELEN+1];
|
|
unsigned int i, len = sizeof(buf);
|
|
char *p = buf;
|
|
|
|
len--; /* allow for trailing \0 */
|
|
if (len < 3)
|
|
return "???";
|
|
for (i = 0 ; i < cookie->len ; i++) {
|
|
if (len < 2) {
|
|
strcpy(p-3, "...");
|
|
break;
|
|
}
|
|
sprintf(p, "%02x", cookie->data[i]);
|
|
p += 2;
|
|
len -= 2;
|
|
}
|
|
*p = '\0';
|
|
|
|
return buf;
|
|
}
|
|
#endif
|