On Fri, Apr 03, 2020 at 11:07:48AM +0200, Martin Pieuchot wrote:
> On 02/04/20(Thu) 13:44, Vitaliy Makkoveev wrote:
> > pipex(4) has pipex_ioctl() interface for pipex_session related routines.
> > pipex_ioctl() calls should be done with pipex_iface_contex, so any
> > operations should be done with pipex_sessions owned by passed
> > pipex_iface_contex. pipex_session check ownership is missing within
> > pipex_ioctl() so anybody can do pipex_ioctl() commands PIPEXDSESSION,
> > PIPEXCSESSION, PIPEXGSTAT and PIPEXGCLOSED on any pipex_session.
> > PIPEXDSESSION on foreign pppx(4) owned pipex_session will crash kernel.
> > Code to reproduce and screenshot attached below. Diffs below add
> > pipes_session ownrship check to pipex_ioctl() internals.
> 
> Awesome bug report!  Fix makes sense to me.
> 
> To avoid check duplication would it make sense to have pppxioctl() call
> pipex_ioctl() for a subset of ioctl(2) values? 

It makes sense, but not now. pppxioctl() should find pipex_iface_context
associated with pppx_if and only then call pipex_ioctl(). For
PIPEXASESSION command pipex_iface_context should be created and for
PIPEXDSESSION it should be deleted outside pipex(4). Current pipex(4)
design is: one pipex_iface_context, one ifnet and many pipex_sessions
associated with this pipex_iface_context use this one ifnet. This is ok
for pppac(4), but for pppx(4) it requres to have one pipex_iface_context
per each pppx_if that's overkilling. I have an idea to move ifnet out from
pipex_iface_context and put it directly (really with some wrapper) to
pipex_session. So pipex_sessions will have individual ifnet (for pppx(4))
or share it (for pppac(4)). pppx_dev will have only one
pipex_iface_context for all its pppx_ifs. After this step PIPEXCSESSION,
PIPEXGSTAT and PIPEXGCLOSED can be executed by pipex_ioctl() directly.

The next step I suggest is to destroy pppx_if context within ifnet's
detach hook. pipex_session will call ifnet_detach() by itself and clear
pppx_if. After this step PIPEXDSESSION calls will be allowed directly on
pipex_iface_context and in-kernel timeout feature will be restored.

Also we have PIPEXSIFDESCR command which is not supported by
pipex_ioctl() :)

But at first, I suggest to add to pppx_add_session() and to
pipex_add_session() check: is the session with given req->pr_protocol and
req->pr_session_id already exist.

At second, return to https://marc.info/?t=158540788400001&r=1&w=2 and
finish cleanups and deduplication.
> 
> > ---- cut begin
> > 
> > #include <sys/types.h>
> > #include <sys/ioctl.h>
> > #include <sys/socket.h>
> > #include <sys/stat.h>
> > #include <stdio.h>
> > #include <err.h>
> > #include <fcntl.h>
> > #include <string.h>
> > #include <unistd.h>
> > 
> > #include <arpa/inet.h>
> > #include <netinet/in.h>
> > #include <net/if.h>
> > #include <net/pipex.h>
> > 
> > #define PIPEX_CLOSE_TIMEOUT 30
> > 
> > int main(void)
> > {
> >     int fd_pppx, fd_pppac;
> >     struct pipex_session_req reqa;
> >     struct pipex_session_close_req reqd;
> > 
> >     if((fd_pppx=open("/dev/pppx0", O_RDWR))<0){
> >             err(1, "open(pppx0)");
> >     }
> > 
> >     if((fd_pppac=open("/dev/pppac0", O_RDWR))<0){
> >             err(1, "open(pppac0)");
> >     }
> > 
> >     memset(&reqa, 0, sizeof(reqa));
> >     reqa.pr_protocol=PIPEX_PROTO_L2TP;
> >     reqa.pr_peer_mru=1024;
> >     reqa.pr_local_address.ss_family=AF_INET;
> >     reqa.pr_local_address.ss_len=sizeof(struct sockaddr_in);
> >     reqa.pr_peer_address.ss_family=AF_INET;
> >     reqa.pr_peer_address.ss_len=sizeof(struct sockaddr_in);
> >     inet_aton("10.0.0.1", &reqa.pr_ip_srcaddr);
> >     inet_aton("10.0.0.2", &reqa.pr_ip_address);
> >     inet_aton("255.255.255.255", &reqa.pr_ip_netmask);
> > 
> >     if(ioctl(fd_pppx, PIPEXASESSION, &reqa)<0){
> >             err(1, "ioctl(fd_pppx, PIPEXASESSION)");
> >     }
> > 
> >     memset(&reqd, 0, sizeof(reqd));
> >     reqd.pcr_protocol=PIPEX_PROTO_L2TP;
> > 
> >     if(ioctl(fd_pppac, PIPEXDSESSION, &reqd)<0){
> >             err(1, "ioctl(fd_ppaac, PIPEDSESSION)");
> >     }
> > 
> >     sleep(PIPEX_CLOSE_TIMEOUT+1);
> > 
> >     return 0;
> > }
> > 
> > ---- cut end
> > 
> > This diff fix pipex_ioctl(PIPEXDSESSION) crash issue
> > 
> > ---- cut begin
> > diff --git sys/net/pipex.c sys/net/pipex.c
> > index 3da8ed8..22edce3 100644
> > --- sys/net/pipex.c
> > +++ sys/net/pipex.c
> > @@ -230,7 +230,7 @@ pipex_ioctl(struct pipex_iface_context *pipex_iface, 
> > u_long cmd, caddr_t data)
> >  
> >     case PIPEXDSESSION:
> >             ret = pipex_close_session(
> > -               (struct pipex_session_close_req *)data);
> > +               (struct pipex_session_close_req *)data, pipex_iface);
> >             break;
> >  
> >     case PIPEXCSESSION:
> > @@ -489,7 +489,8 @@ pipex_notify_close_session_all(void)
> >  }
> >  
> >  Static int
> > -pipex_close_session(struct pipex_session_close_req *req)
> > +pipex_close_session(struct pipex_session_close_req *req,
> > +    struct pipex_iface_context *iface)
> >  {
> >     struct pipex_session *session;
> >  
> > @@ -498,6 +499,8 @@ pipex_close_session(struct pipex_session_close_req *req)
> >         req->pcr_session_id);
> >     if (session == NULL)
> >             return (EINVAL);
> > +   if (session->pipex_iface != iface)
> > +           return (EINVAL);
> >  
> >     /* remove from close_wait list */
> >     if (session->state == PIPEX_STATE_CLOSE_WAIT)
> > diff --git sys/net/pipex_local.h sys/net/pipex_local.h
> > index cf02c8e..ad3c3d3 100644
> > --- sys/net/pipex_local.h
> > +++ sys/net/pipex_local.h
> > @@ -369,7 +369,8 @@ extern struct pipex_hash_head   pipex_id_hashtable[];
> >  void                  pipex_iface_start (struct pipex_iface_context *);
> >  void                  pipex_iface_stop (struct pipex_iface_context *);
> >  int                   pipex_add_session (struct pipex_session_req *, 
> > struct pipex_iface_context *);
> > -int                   pipex_close_session (struct pipex_session_close_req 
> > *);
> > +int                   pipex_close_session (struct pipex_session_close_req 
> > *,
> > +                          struct pipex_iface_context *);
> >  int                   pipex_config_session (struct 
> > pipex_session_config_req *);
> >  int                   pipex_get_stat (struct pipex_session_stat_req *);
> >  int                   pipex_get_closed (struct pipex_session_list_req *);
> > ---- cut end
> > 
> > This diff add ownership checks to the rest pipex_ioctl() commands. A few
> > words about pppx_get_closed(): since in-kernel timeout feature was
> > disabled for pppx(4) related pipex_sessions, closed pipex_sessions can't
> > exist in system, so this function is dummy. I have an idea how to
> > reenable this disabled timeout, but some reafactoring requited, and fair
> > pipex_ioctl(PIPEXGCLOSED) call will be restored.
> > 
> > ---- cut begin
> > diff --git sys/net/if_pppx.c sys/net/if_pppx.c
> > index 37a6af0..6c4977d 100644
> > --- sys/net/if_pppx.c
> > +++ sys/net/if_pppx.c
> > @@ -175,6 +175,12 @@ int            pppx_add_session(struct pppx_dev *,
> >                 struct pipex_session_req *);
> >  int                pppx_del_session(struct pppx_dev *,
> >                 struct pipex_session_close_req *);
> > +int                pppx_config_session(struct pppx_dev *,
> > +               struct pipex_session_config_req *);
> > +int                pppx_get_stat(struct pppx_dev *,
> > +               struct pipex_session_stat_req *);
> > +int                pppx_get_closed(struct pppx_dev *,
> > +               struct pipex_session_list_req *);
> >  int                pppx_set_session_descr(struct pppx_dev *,
> >                 struct pipex_session_descr_req *);
> >  
> > @@ -451,16 +457,18 @@ pppxioctl(dev_t dev, u_long cmd, caddr_t addr, int 
> > flags, struct proc *p)
> >             break;
> >  
> >     case PIPEXCSESSION:
> > -           error = pipex_config_session(
> > +           error = pppx_config_session(pxd,
> >                 (struct pipex_session_config_req *)addr);
> >             break;
> >  
> >     case PIPEXGSTAT:
> > -           error = pipex_get_stat((struct pipex_session_stat_req *)addr);
> > +           error = pppx_get_stat(pxd,
> > +               (struct pipex_session_stat_req *)addr);
> >             break;
> >  
> >     case PIPEXGCLOSED:
> > -           error = pipex_get_closed((struct pipex_session_list_req *)addr);
> > +           error = pppx_get_closed(pxd,
> > +               (struct pipex_session_list_req *)addr);
> >             break;
> >  
> >     case PIPEXSIFDESCR:
> > @@ -947,6 +955,40 @@ pppx_del_session(struct pppx_dev *pxd, struct 
> > pipex_session_close_req *req)
> >     return (0);
> >  }
> >  
> > +int
> > +pppx_config_session(struct pppx_dev *pxd,
> > +    struct pipex_session_config_req *req)
> > +{
> > +   struct pppx_if *pxi;
> > +
> > +   pxi = pppx_if_find(pxd, req->pcr_session_id, req->pcr_protocol);
> > +   if (pxi == NULL)
> > +           return (EINVAL);
> > +
> > +   return pipex_config_session(req, &pxi->pxi_ifcontext);
> > +}
> > +
> > +int
> > +pppx_get_stat(struct pppx_dev *pxd, struct pipex_session_stat_req *req)
> > +{
> > +   struct pppx_if *pxi;
> > +
> > +   pxi = pppx_if_find(pxd, req->psr_session_id, req->psr_protocol);
> > +   if (pxi == NULL)
> > +           return (EINVAL);
> > +
> > +   return pipex_get_stat(req, &pxi->pxi_ifcontext);
> > +}
> > +
> > +int
> > +pppx_get_closed(struct pppx_dev *pxd, struct pipex_session_list_req *req)
> > +{
> > +   /* XXX: Only opened sessions exist for pppx(4) */
> > +   memset(req, 0, sizeof(*req));
> > +
> > +   return 0;
> > +}
> > +
> >  int
> >  pppx_set_session_descr(struct pppx_dev *pxd,
> >      struct pipex_session_descr_req *req)
> > diff --git sys/net/pipex.c sys/net/pipex.c
> > index 22edce3..219e18d 100644
> > --- sys/net/pipex.c
> > +++ sys/net/pipex.c
> > @@ -235,15 +235,17 @@ pipex_ioctl(struct pipex_iface_context *pipex_iface, 
> > u_long cmd, caddr_t data)
> >  
> >     case PIPEXCSESSION:
> >             ret = pipex_config_session(
> > -               (struct pipex_session_config_req *)data);
> > +               (struct pipex_session_config_req *)data, pipex_iface);
> >             break;
> >  
> >     case PIPEXGSTAT:
> > -           ret = pipex_get_stat((struct pipex_session_stat_req *)data);
> > +           ret = pipex_get_stat((struct pipex_session_stat_req *)data,
> > +               pipex_iface);
> >             break;
> >  
> >     case PIPEXGCLOSED:
> > -           ret = pipex_get_closed((struct pipex_session_list_req *)data);
> > +           ret = pipex_get_closed((struct pipex_session_list_req *)data,
> > +               pipex_iface);
> >             break;
> >  
> >     default:
> > @@ -514,7 +516,8 @@ pipex_close_session(struct pipex_session_close_req *req,
> >  }
> >  
> >  Static int
> > -pipex_config_session(struct pipex_session_config_req *req)
> > +pipex_config_session(struct pipex_session_config_req *req,
> > +    struct pipex_iface_context *iface)
> >  {
> >     struct pipex_session *session;
> >  
> > @@ -523,36 +526,44 @@ pipex_config_session(struct pipex_session_config_req 
> > *req)
> >         req->pcr_session_id);
> >     if (session == NULL)
> >             return (EINVAL);
> > +   if (session->pipex_iface != iface)
> > +           return (EINVAL);
> >     session->ip_forward = req->pcr_ip_forward;
> >  
> >     return (0);
> >  }
> >  
> >  Static int
> > -pipex_get_stat(struct pipex_session_stat_req *req)
> > +pipex_get_stat(struct pipex_session_stat_req *req,
> > +    struct pipex_iface_context *iface)
> >  {
> >     struct pipex_session *session;
> >  
> >     NET_ASSERT_LOCKED();
> >     session = pipex_lookup_by_session_id(req->psr_protocol,
> >         req->psr_session_id);
> > -   if (session == NULL) {
> > +   if (session == NULL)
> > +           return (EINVAL);
> > +   if (session->pipex_iface != iface)
> >             return (EINVAL);
> > -   }
> >     req->psr_stat = session->stat;
> >  
> >     return (0);
> >  }
> >  
> >  Static int
> > -pipex_get_closed(struct pipex_session_list_req *req)
> > +pipex_get_closed(struct pipex_session_list_req *req,
> > +    struct pipex_iface_context *iface)
> >  {
> > -   struct pipex_session *session;
> > +   struct pipex_session *session, *session_next;
> >  
> >     NET_ASSERT_LOCKED();
> >     bzero(req, sizeof(*req));
> > -   while (!LIST_EMPTY(&pipex_close_wait_list)) {
> > -           session = LIST_FIRST(&pipex_close_wait_list);
> > +   for (session = LIST_FIRST(&pipex_close_wait_list);
> > +       session; session = session_next) {
> > +           session_next = LIST_NEXT(session, state_list);
> > +           if (session->pipex_iface != iface)
> > +                   continue;
> >             req->plr_ppp_id[req->plr_ppp_id_count++] = session->ppp_id;
> >             LIST_REMOVE(session, state_list);
> >             session->state = PIPEX_STATE_CLOSE_WAIT2;
> > diff --git sys/net/pipex_local.h sys/net/pipex_local.h
> > index ad3c3d3..c124eb9 100644
> > --- sys/net/pipex_local.h
> > +++ sys/net/pipex_local.h
> > @@ -371,9 +371,12 @@ void                  pipex_iface_stop (struct 
> > pipex_iface_context *);
> >  int                   pipex_add_session (struct pipex_session_req *, 
> > struct pipex_iface_context *);
> >  int                   pipex_close_session (struct pipex_session_close_req 
> > *,
> >                            struct pipex_iface_context *);
> > -int                   pipex_config_session (struct 
> > pipex_session_config_req *);
> > -int                   pipex_get_stat (struct pipex_session_stat_req *);
> > -int                   pipex_get_closed (struct pipex_session_list_req *);
> > +int                   pipex_config_session (struct 
> > pipex_session_config_req *,
> > +                          struct pipex_iface_context *);
> > +int                   pipex_get_stat (struct pipex_session_stat_req *,
> > +                          struct pipex_iface_context *);
> > +int                   pipex_get_closed (struct pipex_session_list_req *,
> > +                          struct pipex_iface_context *);
> >  int                   pipex_destroy_session (struct pipex_session *);
> >  struct pipex_session  *pipex_lookup_by_ip_address (struct in_addr);
> >  struct pipex_session  *pipex_lookup_by_session_id (int, int);
> > ---- cut end
> 
> 

Reply via email to