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.