title: Oops while traversing sk_write_queue in Linux kernel
body: <p>My Linux 3.8.8 kernel would Oops while traversing tcp sk_write_queue in kernel, like below:</p>
  
  <pre><code>BUG: unable to handle kernel paging request at 00b85055
  </code></pre>
  
  <p>My code logic in kernel is as below:</p>
  
  <ol>
  <li>Get struct socket from socket file descriptor;</li>
  <li>Get struct sock from struct socket;</li>
  <li>lock_sock();</li>
  <li>Get sk_write_queue from struct sock;</li>
  <li>traverse sk_write_queue, read linked skb buffer;</li>
  <li>release_sock();</li>
  <li>return to user space.</li>
  </ol>
  
  <p>While in step (5), sometimes will trigger Oops above, the problem code is:</p>
  
  <pre><code>skb_is_nonlinear(skb);
  </code></pre>
  
  <p>It means that skb pointer now is invalid or null, can't the lock_sock protect the traverse process?</p>
  
  <p>Code:</p>
  
  <pre><code>asmlinkage long sys_tcp_get(int fd, int *id)
  {
      int err, i, j;
      struct socket *sock;
      struct sock *sk;
      struct sk_buff_head *queue;
      struct sk_buff *skb;
      unsigned char buf[4] = {0};
  
      sock = sockfd_lookup(fd, &amp;err);
      if (sock == NULL) return -1;
  
      sk = sock-&gt;sk;
      if (sk == NULL) {
              err = -1;
              goto free_socket;
      }
  
      lock_sock(sk);
  
      if (((1 &lt;&lt; sk-&gt;sk_state) &amp; ~TCPF_ESTABLISHED) ||
              (sk-&gt;sk_err || (sk-&gt;sk_shutdown &amp; SEND_SHUTDOWN))) {
                      err = -1;
                      goto release_socket;
      }
  
      queue = &amp;(sk-&gt;sk_write_queue);
      if (tcp_send_head(sk) == NULL) {
              err = -1;
              goto release_socket;
      }
  
      for(skb = tcp_send_head(sk); skb != (struct sk_buff *)(queue); skb = skb-&gt;next) {
              if (skb_is_nonlinear(skb)) {
                      err = -1;
                      goto release_socket;
              }
  
              i = 0;
              j = 0;
              while(i &lt; skb-&gt;len) {
                      buf[j++] = skb-&gt;data[i++];
                      if (j == 4) {
                              if (buf[0] == 1 &amp;&amp; buf[1] == 2 &amp;&amp; buf[2] == 3) {
                                     *id = buf[3];
                                      err = 0;
                                      goto release_socket;
                              } else {
                                      j = 0;
                              }
                      }
              }
      }
  
  release_socket:
      release_sock(sk);
  
  free_socket:
      sockfd_put(sock);
      return err;
  }
  </code></pre>