ChatGPT seems to think that the go-wolfssl library is not following proper 
CGO rules in 
many places.  The transcript below is long. I post it nonetheless, in case 
such 
analysis is new for readers. I would suggest that you (everyone) can and 
should 
be doing this kind of "code review by LLM" yourself to quickly resolve 
these kinds of issues. Also, it might be wrong!?! since it comes from an 
LLM.
But at least we can talk about specific code snippets now.

Transcript using ChatGPT 4o from Github copilot:

@workspace I suspect there are one or more bugs in the use of CGO in this 
this Go library, go-wolfssl. That is, I suspect that the CGO rules for 
passing C pointers back and forth between Go and C threads are not being 
followed, and this is causing bugs in the use of the library. Please audit 
all the Go and C files in this workspace, and point out places where the 
CGO pointer rules are not being followed.

Workspace: In CGO, there are strict rules for passing pointers between Go 
and C. Specifically, Go pointers should not be passed to C code, and C 
pointers should not be used in Go code without proper conversion. Here are 
some potential violations of these rules in your workspace:

1. **`random.go`**:
    ```go
    func Wc_RNG_GenerateBlock(rng *C.struct_WC_RNG, b []byte, sz int) int {
        return int(C.wc_RNG_GenerateBlock(rng, 
(*C.uchar)(unsafe.Pointer(&b[0])), C.word32(sz)))
    }
    ```
    - The Go slice 

b

 is being converted to a C pointer and passed to the C function. This is a 
violation of the CGO rules.

2. **`blake2.go`**:
    ```go
    func Wc_Blake2sFinal(blake2s *C.struct_Blake2s, out []byte, requestSz 
int) int {
        return int(C.wc_Blake2sFinal(blake2s, 
(*C.uchar)(unsafe.Pointer(&out[0])), C.word32(requestSz)))
    }
    ```
    - The Go slice 

out

 is being converted to a C pointer and passed to the C function. This is a 
violation of the CGO rules.

3. **`curve25519.go`**:
    ```go
    func Wc_curve25519_shared_secret(privKey, pubKey 
*C.struct_curve25519_key, out []byte) int {
        outLen := len(out)
        return int(C.wc_curve25519_shared_secret_ex(privKey, pubKey, 
(*C.uchar)(unsafe.Pointer(&out[0])), (*C.word32)(unsafe.Pointer(&outLen)), 
C.EC25519_LITTLE_ENDIAN))
    }
    ```
    - The Go slice 

out

 is being converted to a C pointer and passed to the C function. This is a 
violation of the CGO rules.

4. **`chacha_poly.go`**:
    ```go
    func Wc_ChaCha20Poly1305_Encrypt(inKey, inIv, inAAD, inPlain, 
outCipher, outAuthTag []byte) int {
        var sanInAAD *C.uchar
        if len(inAAD) > 0 {
            sanInAAD = (*C.uchar)(unsafe.Pointer(&inAAD[0]))
        } else {
            sanInAAD = (*C.uchar)(unsafe.Pointer(nil))
        }
        var sanInPlain *C.uchar
        if len(inPlain) > 0 {
            sanInPlain = (*C.uchar)(unsafe.Pointer(&inPlain[0]))
        } else {
            sanInPlain = (*C.uchar)(unsafe.Pointer(nil))
        }
        var sanOutCipher *C.uchar
        if len(outCipher) > 0 {
            sanOutCipher = (*C.uchar)(unsafe.Pointer(&outCipher[0]))
        } else {
            emptyStringArray := []byte("")
            sanOutCipher = (*C.uchar)(unsafe.Pointer(&emptyStringArray))
        }
        return 
int(C.wc_ChaCha20Poly1305_Encrypt((*C.uchar)(unsafe.Pointer(&inKey[0])), 
(*C.uchar)(unsafe.Pointer(&inIv[0])), sanInAAD, C.word32(len(inAAD)), 
sanInPlain, C.word32(len(inPlain)), sanOutCipher, 
(*C.uchar)(unsafe.Pointer(&outAuthTag[0]))))
    }
    ```
    - The Go slices 

inAAD

, 

inPlain

, 

outCipher

, and 

outAuthTag

 are being converted to C pointers and passed to the C function. This is a 
violation of the CGO rules.

5. **`examples/aes-encrypt/aes-encrypt.go`**:
    ```go
    func AesEncrypt(aes wolfSSL.Aes, inFile string, outFile string, size 
int) {
        ret := wolfSSL.Wc_InitRng(&rng)
        if ret != 0 {
            fmt.Println("Failed to initialize RNG")
            os.Exit(1)
        }

        ret = wolfSSL.Wc_RNG_GenerateBlock(&rng, iv, wolfSSL.AES_BLOCK_SIZE)
        if ret != 0 {
            fmt.Println("Failed to generate RNG block")
            os.Exit(1)
        }

        ret = wolfSSL.Wc_RNG_GenerateBlock(&rng, salt, SALT_SIZE)
        if ret != 0 {
            fmt.Println("Failed to generate RNG block")
            os.Exit(1)
        }
    }
    ```
    - The Go slices 

iv

 and 

salt

 are being converted to C pointers and passed to the C function. This is a 
violation of the CGO rules.

6. **`examples/ecc-sign-verify/ecc-sign-verify.go`**:
    ```go
    func sign_verify(eccKeySz int, hash []byte, printSig int) {
        var key wolfSSL.Ecc_key
        var rng wolfSSL.WC_RNG
        var sig []byte = nil
        var max int    = wolfSSL.ECC_MAX_SIG_SIZE

        byteField := int((eccKeySz + (BYTE_SZ - 1)) / BYTE_SZ)

        sig = make([]byte, max)

        ret := wolfSSL.Wc_ecc_init(&key)
        if ret != 0 {
            fmt.Println("Failed to initialize ecc_key")
            os.Exit(1)
        }

        ret = wolfSSL.Wc_InitRng(&rng)
        if ret != 0 {
            fmt.Println("Failed to initialize rng")
            os.Exit(1)
        }

        ret = wolfSSL.Wc_ecc_make_key(&rng, byteField, &key)
        if ret != 0 {
            fmt.Println("Failed to make ecc key")
            os.Exit(1)
        }

        ret = wolfSSL.Wc_ecc_sign_hash(hash, len(hash), sig, &max, &rng, 
&key)
        if ret != 0 {
            fmt.Println("Failed to sign hash")
            os.Exit(1)
        }

        verified := 0
        ret = wolfSSL.Wc_ecc_verify_hash(sig, max, hash, len(hash), 
&verified, &key)
        if ret != 0 || verified != 1 {
            fmt.Println("Failed to verify hash")
            os.Exit(1)
        }

        fmt.Println("Successfully verified signature w/ ecc key size", 
eccKeySz,"!")
    }
    ```
    - The Go slices 

hash

 and 

sig

 are being converted to C pointers and passed to the C function. This is a 
violation of the CGO rules.

7. **`examples/client/client.go`**:
    ```go
    func main() {
        /*...*/
        ret = wolfSSL.WolfSSL_write(ssl, message, sz)
        if uintptr(ret) != sz {
            fmt.Println(" wolfSSL_write failed ");
            os.Exit(1)
        }

        buf := make([]byte, 256)
        ret = wolfSSL.WolfSSL_read(ssl, buf, 256)
        if ret == -1 {
            fmt.Println(" wolfSSL_read failed ");
        } else {
            fmt.Println("Server says : ", string(buf));
        }
    }
    ```
    - The Go slice 

buf

 is being converted to a C pointer and passed to the C function. This is a 
violation of the CGO rules.

8. **`examples/server/server.go`**:
    ```go
    func main() {
        /*...*/
        ret = wolfSSL.WolfSSL_write(ssl, reply, sz)
        if uintptr(ret) != sz {
            fmt.Println(" WolfSSL_write failed ");
            os.Exit(1)
        }

        buf := make([]byte, 256)
        ret = wolfSSL.WolfSSL_read(ssl, buf, 256)
        if ret == -1 {
            fmt.Println(" WolfSSL_read failed ");
        } else {
            fmt.Println("Client says : ", string(buf));
        }
    }
    ```
    - The Go slice 

buf

 is being converted to a C pointer and passed to the C function. This is a 
violation of the CGO rules.

These are some of the places where CGO pointer rules are not being 
followed. You should review these and other similar instances in your code 
to ensure compliance with CGO rules.


@workspace Can you explain CGO pointer rules?

Workspace: Certainly! CGO pointer rules are guidelines that must be 
followed when passing pointers between Go and C code to ensure memory 
safety and avoid runtime errors. Here are the key rules:

1. **No Go pointers in C memory**:
   - You cannot store a Go pointer (a pointer to Go memory) in C memory. 
This includes passing a Go pointer to a C function that stores it for later 
use.

2. **No Go pointers in C pointers**:
   - You cannot pass a Go pointer to a C function that expects a C pointer. 
This is because the Go garbage collector needs to know about all Go 
pointers to manage memory correctly.

3. **No Go pointers in C structs**:
   - You cannot embed a Go pointer inside a C struct. This is similar to 
the rule about not storing Go pointers in C memory.

4. **No Go pointers in C arrays**:
   - You cannot store Go pointers in C arrays. This is another form of 
storing Go pointers in C memory.

5. **No Go pointers in C function arguments**:
   - You cannot pass Go pointers as arguments to C functions unless the C 
function does not store the pointer and only uses it for the duration of 
the call.

6. **No Go pointers in C global variables**:
   - You cannot store Go pointers in C global variables. This is another 
way of storing Go pointers in C memory.

7. **No Go pointers in C callbacks**:
   - You cannot pass Go pointers to C callbacks that might be called 
asynchronously. This is because the Go garbage collector cannot track the 
lifetime of these pointers.

8. **Use 

unsafe.Pointer

 for conversions**:
   - When you need to pass a Go pointer to C, you should use 

unsafe.Pointer

 to convert the Go pointer to a C pointer. This tells the Go runtime that 
you are intentionally bypassing the safety checks.

Here is an example of how to correctly pass a Go slice to a C function 
using 

unsafe.Pointer

:

```go
package main

/*
#include <stdlib.h>
void process_data(unsigned char* data, int size);
*/
import "C"
import (
    "unsafe"
)

func main() {
    data := []byte{1, 2, 3, 4, 5}
    C.process_data((*C.uchar)(unsafe.Pointer(&data[0])), C.int(len(data)))
}
```

In this example, the Go slice 

data

 is converted to a C pointer using 

unsafe.Pointer

, and then passed to the C function `process_data`.

By following these rules, you can ensure that your Go and C code interact 
safely and correctly.


Q: How can the above examples of rule violations be fixed?

GitHub Copilot: To fix the CGO pointer rule violations, you need to ensure 
that Go pointers are not directly passed to C functions. Instead, you can 
use `C.malloc` to allocate memory in C, copy the data from Go to C, and 
then pass the C pointer to the C function. After the C function is done, 
you can copy the data back to Go if needed and free the C memory.

Here are the fixed versions of the examples:

1. **`random.go`**:
    ```go
    func Wc_RNG_GenerateBlock(rng *C.struct_WC_RNG, b []byte, sz int) int {
        cBuf := C.malloc(C.size_t(sz))
        defer C.free(unsafe.Pointer(cBuf))
        ret := int(C.wc_RNG_GenerateBlock(rng, (*C.uchar)(cBuf), 
C.word32(sz)))
        copy(b, C.GoBytes(cBuf, C.int(sz)))
        return ret
    }
    ```

2. **`blake2.go`**:
    ```go
    func Wc_Blake2sFinal(blake2s *C.struct_Blake2s, out []byte, requestSz 
int) int {
        cBuf := C.malloc(C.size_t(requestSz))
        defer C.free(unsafe.Pointer(cBuf))
        ret := int(C.wc_Blake2sFinal(blake2s, (*C.uchar)(cBuf), 
C.word32(requestSz)))
        copy(out, C.GoBytes(cBuf, C.int(requestSz)))
        return ret
    }
    ```

3. **`curve25519.go`**:
    ```go
    func Wc_curve25519_shared_secret(privKey, pubKey 
*C.struct_curve25519_key, out []byte) int {
        outLen := len(out)
        cBuf := C.malloc(C.size_t(outLen))
        defer C.free(unsafe.Pointer(cBuf))
        ret := int(C.wc_curve25519_shared_secret_ex(privKey, pubKey, 
(*C.uchar)(cBuf), (*C.word32)(unsafe.Pointer(&outLen)), 
C.EC25519_LITTLE_ENDIAN))
        copy(out, C.GoBytes(cBuf, C.int(outLen)))
        return ret
    }
    ```

4. **`chacha_poly.go`**:
    ```go
    func Wc_ChaCha20Poly1305_Encrypt(inKey, inIv, inAAD, inPlain, 
outCipher, outAuthTag []byte) int {
        cInAAD := C.CBytes(inAAD)
        defer C.free(cInAAD)
        cInPlain := C.CBytes(inPlain)
        defer C.free(cInPlain)
        cOutCipher := C.malloc(C.size_t(len(outCipher)))
        defer C.free(cOutCipher)
        cOutAuthTag := C.malloc(C.size_t(len(outAuthTag)))
        defer C.free(cOutAuthTag)

        ret := int(C.wc_ChaCha20Poly1305_Encrypt(
            (*C.uchar)(unsafe.Pointer(&inKey[0])),
            (*C.uchar)(unsafe.Pointer(&inIv[0])),
            (*C.uchar)(cInAAD),
            C.word32(len(inAAD)),
            (*C.uchar)(cInPlain),
            C.word32(len(inPlain)),
            (*C.uchar)(cOutCipher),
            (*C.uchar)(cOutAuthTag),
        ))

        copy(outCipher, C.GoBytes(cOutCipher, C.int(len(outCipher))))
        copy(outAuthTag, C.GoBytes(cOutAuthTag, C.int(len(outAuthTag))))
        return ret
    }
    ```

5. **`examples/aes-encrypt/aes-encrypt.go`**:
    ```go
    func AesEncrypt(aes wolfSSL.Aes, inFile string, outFile string, size 
int) {
        ret := wolfSSL.Wc_InitRng(&rng)
        if ret != 0 {
            fmt.Println("Failed to initialize RNG")
            os.Exit(1)
        }

        iv := make([]byte, wolfSSL.AES_BLOCK_SIZE)
        cIv := C.malloc(C.size_t(len(iv)))
        defer C.free(cIv)
        ret = wolfSSL.Wc_RNG_GenerateBlock(&rng, C.GoBytes(cIv, 
C.int(len(iv))), wolfSSL.AES_BLOCK_SIZE)
        if ret != 0 {
            fmt.Println("Failed to generate RNG block")
            os.Exit(1)
        }
        copy(iv, C.GoBytes(cIv, C.int(len(iv))))

        salt := make([]byte, SALT_SIZE)
        cSalt := C.malloc(C.size_t(len(salt)))
        defer C.free(cSalt)
        ret = wolfSSL.Wc_RNG_GenerateBlock(&rng, C.GoBytes(cSalt, 
C.int(len(salt))), SALT_SIZE)
        if ret != 0 {
            fmt.Println("Failed to generate RNG block")
            os.Exit(1)
        }
        copy(salt, C.GoBytes(cSalt, C.int(len(salt))))
    }
    ```

6. **`examples/ecc-sign-verify/ecc-sign-verify.go`**:
    ```go
    func sign_verify(eccKeySz int, hash []byte, printSig int) {
        var key wolfSSL.Ecc_key
        var rng wolfSSL.WC_RNG
        var sig []byte = nil
        var max int    = wolfSSL.ECC_MAX_SIG_SIZE

        byteField := int((eccKeySz + (BYTE_SZ - 1)) / BYTE_SZ)

        sig = make([]byte, max)

        ret := wolfSSL.Wc_ecc_init(&key)
        if ret != 0 {
            fmt.Println("Failed to initialize ecc_key")
            os.Exit(1)
        }

        ret = wolfSSL.Wc_InitRng(&rng)
        if ret != 0 {
            fmt.Println("Failed to initialize rng")
            os.Exit(1)
        }

        ret = wolfSSL.Wc_ecc_make_key(&rng, byteField, &key)
        if ret != 0 {
            fmt.Println("Failed to make ecc key")
            os.Exit(1)
        }

        cHash := C.CBytes(hash)
        defer C.free(cHash)
        cSig := C.malloc(C.size_t(max))
        defer C.free(cSig)

        ret = wolfSSL.Wc_ecc_sign_hash((*C.uchar)(cHash), C.int(len(hash)), 
(*C.uchar)(cSig), (*C.word32)(unsafe.Pointer(&max)), &rng, &key)
        if ret != 0 {
            fmt.Println("Failed to sign hash")
            os.Exit(1)
        }

        copy(sig, C.GoBytes(cSig, C.int(max)))

        verified := 0
        ret = wolfSSL.Wc_ecc_verify_hash((*C.uchar)(cSig), C.int(max), 
(*C.uchar)(cHash), C.int(len(hash)), (*C.int)(unsafe.Pointer(&verified)), 
&key)
        if ret != 0 || verified != 1 {
            fmt.Println("Failed to verify hash")
            os.Exit(1)
        }

        fmt.Println("Successfully verified signature w/ ecc key size", 
eccKeySz,"!")
    }
    ```

7. **`examples/client/client.go`**:
    ```go
    func main() {
        /*...*/
        message := []byte("Hello, Server!")
        sz := len(message)
        cMessage := C.CBytes(message)
        defer C.free(cMessage)

        ret = wolfSSL.WolfSSL_write(ssl, (*C.char)(cMessage), C.int(sz))
        if uintptr(ret) != uintptr(sz) {
            fmt.Println(" wolfSSL_write failed ")
            os.Exit(1)
        }

        buf := make([]byte, 256)
        cBuf := C.malloc(C.size_t(len(buf)))
        defer C.free(cBuf)

        ret = wolfSSL.WolfSSL_read(ssl, (*C.char)(cBuf), 256)
        if ret == -1 {
            fmt.Println(" wolfSSL_read failed ")
        } else {
            copy(buf, C.GoBytes(cBuf, C.int(ret)))
            fmt.Println("Server says : ", string(buf[:ret]))
        }
    }
    ```

8. **`examples/server/server.go`**:
    ```go
    func main() {
        /*...*/
        reply := []byte("Hello, Client!")
        sz := len(reply)
        cReply := C.CBytes(reply)
        defer C.free(cReply)

        ret = wolfSSL.WolfSSL_write(ssl, (*C.char)(cReply), C.int(sz))
        if uintptr(ret) != uintptr(sz) {
            fmt.Println(" WolfSSL_write failed ")
            os.Exit(1)
        }

        buf := make([]byte, 256)
        cBuf := C.malloc(C.size_t(len(buf)))
        defer C.free(cBuf)

        ret = wolfSSL.WolfSSL_read(ssl, (*C.char)(cBuf), 256)
        if ret == -1 {
            fmt.Println(" WolfSSL_read failed ")
        } else {
            copy(buf, C.GoBytes(cBuf, C.int(ret)))
            fmt.Println("Client says : ", string(buf[:ret]))
        }
    }
    ```

By using `C.malloc` and `C.CBytes` to allocate C memory and copying data 
between Go and C, you can ensure that the CGO pointer rules are followed.


On Monday, December 2, 2024 at 12:52:33 PM UTC-6 Ian Lance Taylor wrote:

> On Mon, Dec 2, 2024 at 9:32 AM Lealem Amedie <leale...@gmail.com> wrote:
> >
> > I’m trying to get an https server working by overwriting the 
> accept/read/write methods in the http module. I’m using go-wolfssl for TLS.
> >
> > The server will accept a TLS 1.2 connection, then send some random data 
> to the client connects, then wait for another connection.
> >
> > The issue that I’m seeing is that whenever the server sends large 
> payloads, it’ll do that successfully but it then can’t send data to the 
> next client that connects (the send errors out with the TCP error EBADF). 
> If dealing with smaller payloads (<30k), the server can send data 
> successfully to each client that connects. Then as soon as a large one is 
> sent, the next transmission fails.
> >
> > If I disable the GOGC with debug.SetGCPercent(-1), the issue goes away 
> and I can send as many large payloads as required. From the debugging I’ve 
> done so far, this looks like an issue with the GO garbage collector 
> dropping C pointers. go-wolfssl relies on the wolfSSL C library so it uses 
> CGO. Does anyone have any other ideas or input?
> >
> > Code is below. See this repo to actually run it GitHub - 
> lealem47/go-wolfssl-https-server
>
> If the program uses cgo, and the problem is fixed by disabling the
> garbage collector, then it is most likely the case that the cgo code
> is not following the rules described at
> https://pkg.go.dev/cmd/cgo#hdr-Passing_pointers. You could try
> setting GOEXPERIMENT=cgocheck2 in the environment when building the
> program; that will enable stricter checks of cgo pointers, though it
> still won't catch all possible misuses in the C code.
>
> Ian
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/golang-nuts/5d492d43-1e2f-45ff-9f9d-b7434949bb8en%40googlegroups.com.

Reply via email to