From 0002ad0fb4eb151e60397ee1a0618766a3e60269 Mon Sep 17 00:00:00 2001 From: Stone-afk <73482944+Stone-afk@users.noreply.github.com> Date: Thu, 18 May 2023 21:22:41 +0800 Subject: [PATCH] bugfix: protect field access with lock to avoid possible data race (#5211) --- CHANGELOG.md | 1 + client/cache/README.md | 54 ------------------- .../web/session/couchbase/sess_couchbase.go | 13 +++-- server/web/session/ledis/ledis_session.go | 5 +- server/web/session/memcache/sess_memcache.go | 12 +++-- server/web/session/mysql/sess_mysql.go | 13 +++-- .../web/session/postgres/sess_postgresql.go | 14 ++--- server/web/session/redis/sess_redis.go | 20 ++++--- .../session/redis_cluster/redis_cluster.go | 12 +++-- .../redis_sentinel/sess_redis_sentinel.go | 11 ++-- server/web/session/sess_cookie.go | 19 ++++--- server/web/session/ssdb/sess_ssdb.go | 5 +- 12 files changed, 81 insertions(+), 98 deletions(-) delete mode 100644 client/cache/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index f4775697..1c4becd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - [Fix 5079: only log msg when the channel is not closed](https://github.com/beego/beego/pull/5132) - [Fix 4435: Controller SaveToFile remove all temp file](https://github.com/beego/beego/pull/5138) - [Fix 5079: Split signalChan into flushChan and closeChan](https://github.com/beego/beego/pull/5139) +- [Fix 5172: protect field access with lock to avoid possible data race](https://github.com/beego/beego/pull/5210) # v2.0.7 - [Upgrade github.com/go-kit/kit, CVE-2022-24450](https://github.com/beego/beego/pull/5121) diff --git a/client/cache/README.md b/client/cache/README.md deleted file mode 100644 index df1ea095..00000000 --- a/client/cache/README.md +++ /dev/null @@ -1,54 +0,0 @@ -## cache - -cache is a Go cache manager. It can use many cache adapters. The repo is inspired by `database/sql` . - -## How to install? - - go get github.com/beego/beego/v2/client/cache - -## What adapters are supported? - -As of now this cache support memory, Memcache and Redis. - -## How to use it? - -First you must import it - - import ( - "github.com/beego/beego/v2/client/cache" - ) - -Then init a Cache (example with memory adapter) - - bm, err := cache.NewCache("memory", `{"interval":60}`) - -Use it like this: - - bm.Put("astaxie", 1, 10 * time.Second) - bm.Get("astaxie") - bm.IsExist("astaxie") - bm.Delete("astaxie") - -## Memory adapter - -Configure memory adapter like this: - - {"interval":60} - -interval means the gc time. The cache will check at each time interval, whether item has expired. - -## Memcache adapter - -Memcache adapter use the [gomemcache](http://github.com/bradfitz/gomemcache) client. - -Configure like this: - - {"conn":"127.0.0.1:11211"} - -## Redis adapter - -Redis adapter use the [redigo](http://github.com/gomodule/redigo) client. - -Configure like this: - - {"conn":":6039"} diff --git a/server/web/session/couchbase/sess_couchbase.go b/server/web/session/couchbase/sess_couchbase.go index 6c96b5b5..6b464e55 100644 --- a/server/web/session/couchbase/sess_couchbase.go +++ b/server/web/session/couchbase/sess_couchbase.go @@ -20,15 +20,16 @@ // // Usage: // import( -// _ "github.com/beego/beego/v2/server/web/session/couchbase" -// "github.com/beego/beego/v2/server/web/session" +// +// _ "github.com/beego/beego/v2/server/web/session/couchbase" +// "github.com/beego/beego/v2/server/web/session" +// // ) // // func init() { // globalSessions, _ = session.NewManager("couchbase", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"http://host:port/, Pool, Bucket"}``) // go globalSessions.GC() // } -// package couchbase import ( @@ -105,8 +106,10 @@ func (cs *SessionStore) SessionID(context.Context) string { // SessionRelease Write couchbase session with Gob string func (cs *SessionStore) SessionRelease(ctx context.Context, w http.ResponseWriter) { defer cs.b.Close() - - bo, err := session.EncodeGob(cs.values) + cs.lock.RLock() + values := cs.values + cs.lock.RUnlock() + bo, err := session.EncodeGob(values) if err != nil { return } diff --git a/server/web/session/ledis/ledis_session.go b/server/web/session/ledis/ledis_session.go index 80524e20..5776fdc2 100644 --- a/server/web/session/ledis/ledis_session.go +++ b/server/web/session/ledis/ledis_session.go @@ -69,7 +69,10 @@ func (ls *SessionStore) SessionID(context.Context) string { // SessionRelease save session values to ledis func (ls *SessionStore) SessionRelease(ctx context.Context, w http.ResponseWriter) { - b, err := session.EncodeGob(ls.values) + ls.lock.RLock() + values := ls.values + ls.lock.RUnlock() + b, err := session.EncodeGob(values) if err != nil { return } diff --git a/server/web/session/memcache/sess_memcache.go b/server/web/session/memcache/sess_memcache.go index 46300dd3..05f33176 100644 --- a/server/web/session/memcache/sess_memcache.go +++ b/server/web/session/memcache/sess_memcache.go @@ -20,15 +20,16 @@ // // Usage: // import( -// _ "github.com/beego/beego/v2/server/web/session/memcache" -// "github.com/beego/beego/v2/server/web/session" +// +// _ "github.com/beego/beego/v2/server/web/session/memcache" +// "github.com/beego/beego/v2/server/web/session" +// // ) // // func init() { // globalSessions, _ = session.NewManager("memcache", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:11211"}``) // go globalSessions.GC() // } -// package memcache import ( @@ -96,7 +97,10 @@ func (rs *SessionStore) SessionID(context.Context) string { // SessionRelease save session values to memcache func (rs *SessionStore) SessionRelease(ctx context.Context, w http.ResponseWriter) { - b, err := session.EncodeGob(rs.values) + rs.lock.RLock() + values := rs.values + rs.lock.RUnlock() + b, err := session.EncodeGob(values) if err != nil { return } diff --git a/server/web/session/mysql/sess_mysql.go b/server/web/session/mysql/sess_mysql.go index 2ed2354a..033868d4 100644 --- a/server/web/session/mysql/sess_mysql.go +++ b/server/web/session/mysql/sess_mysql.go @@ -19,6 +19,7 @@ // go install github.com/go-sql-driver/mysql // // mysql session support need create table as sql: +// // CREATE TABLE `session` ( // `session_key` char(64) NOT NULL, // `session_data` blob, @@ -28,15 +29,16 @@ // // Usage: // import( -// _ "github.com/beego/beego/v2/server/web/session/mysql" -// "github.com/beego/beego/v2/server/web/session" +// +// _ "github.com/beego/beego/v2/server/web/session/mysql" +// "github.com/beego/beego/v2/server/web/session" +// // ) // // func init() { // globalSessions, _ = session.NewManager("mysql", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]"}``) // go globalSessions.GC() // } -// package mysql import ( @@ -109,7 +111,10 @@ func (st *SessionStore) SessionID(context.Context) string { // must call this method to save values to database. func (st *SessionStore) SessionRelease(ctx context.Context, w http.ResponseWriter) { defer st.c.Close() - b, err := session.EncodeGob(st.values) + st.lock.RLock() + values := st.values + st.lock.RUnlock() + b, err := session.EncodeGob(values) if err != nil { return } diff --git a/server/web/session/postgres/sess_postgresql.go b/server/web/session/postgres/sess_postgresql.go index 5ce1e114..7d16d296 100644 --- a/server/web/session/postgres/sess_postgresql.go +++ b/server/web/session/postgres/sess_postgresql.go @@ -18,7 +18,6 @@ // // go install github.com/lib/pq // -// // needs this table in your database: // // CREATE TABLE session ( @@ -35,18 +34,18 @@ // SessionSavePath = "user=a password=b dbname=c sslmode=disable" // SessionName = session // -// // Usage: // import( -// _ "github.com/beego/beego/v2/server/web/session/postgresql" -// "github.com/beego/beego/v2/server/web/session" +// +// _ "github.com/beego/beego/v2/server/web/session/postgresql" +// "github.com/beego/beego/v2/server/web/session" +// // ) // // func init() { // globalSessions, _ = session.NewManager("postgresql", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"user=pqgotest dbname=pqgotest sslmode=verify-full"}``) // go globalSessions.GC() // } -// package postgres import ( @@ -115,7 +114,10 @@ func (st *SessionStore) SessionID(context.Context) string { // must call this method to save values to database. func (st *SessionStore) SessionRelease(ctx context.Context, w http.ResponseWriter) { defer st.c.Close() - b, err := session.EncodeGob(st.values) + st.lock.RLock() + values := st.values + st.lock.RUnlock() + b, err := session.EncodeGob(values) if err != nil { return } diff --git a/server/web/session/redis/sess_redis.go b/server/web/session/redis/sess_redis.go index f13e979e..d40745a9 100644 --- a/server/web/session/redis/sess_redis.go +++ b/server/web/session/redis/sess_redis.go @@ -20,15 +20,16 @@ // // Usage: // import( -// _ "github.com/beego/beego/v2/server/web/session/redis" -// "github.com/beego/beego/v2/server/web/session" +// +// _ "github.com/beego/beego/v2/server/web/session/redis" +// "github.com/beego/beego/v2/server/web/session" +// // ) // -// func init() { -// globalSessions, _ = session.NewManager("redis", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:7070"}``) -// go globalSessions.GC() -// } -// +// func init() { +// globalSessions, _ = session.NewManager("redis", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:7070"}``) +// go globalSessions.GC() +// } package redis import ( @@ -100,7 +101,10 @@ func (rs *SessionStore) SessionID(context.Context) string { // SessionRelease save session values to redis func (rs *SessionStore) SessionRelease(ctx context.Context, w http.ResponseWriter) { - b, err := session.EncodeGob(rs.values) + rs.lock.RLock() + values := rs.values + rs.lock.RUnlock() + b, err := session.EncodeGob(values) if err != nil { return } diff --git a/server/web/session/redis_cluster/redis_cluster.go b/server/web/session/redis_cluster/redis_cluster.go index c1c46bcd..c254173e 100644 --- a/server/web/session/redis_cluster/redis_cluster.go +++ b/server/web/session/redis_cluster/redis_cluster.go @@ -20,15 +20,16 @@ // // Usage: // import( -// _ "github.com/beego/beego/v2/server/web/session/redis_cluster" -// "github.com/beego/beego/v2/server/web/session" +// +// _ "github.com/beego/beego/v2/server/web/session/redis_cluster" +// "github.com/beego/beego/v2/server/web/session" +// // ) // // func init() { // globalSessions, _ = session.NewManager("redis_cluster", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:7070;127.0.0.1:7071"}``) // go globalSessions.GC() // } -// package redis_cluster import ( @@ -100,7 +101,10 @@ func (rs *SessionStore) SessionID(context.Context) string { // SessionRelease save session values to redis_cluster func (rs *SessionStore) SessionRelease(ctx context.Context, w http.ResponseWriter) { - b, err := session.EncodeGob(rs.values) + rs.lock.RLock() + values := rs.values + rs.lock.RUnlock() + b, err := session.EncodeGob(values) if err != nil { return } diff --git a/server/web/session/redis_sentinel/sess_redis_sentinel.go b/server/web/session/redis_sentinel/sess_redis_sentinel.go index d18a3773..43ba4c10 100644 --- a/server/web/session/redis_sentinel/sess_redis_sentinel.go +++ b/server/web/session/redis_sentinel/sess_redis_sentinel.go @@ -20,8 +20,10 @@ // // Usage: // import( -// _ "github.com/beego/beego/v2/server/web/session/redis_sentinel" -// "github.com/beego/beego/v2/server/web/session" +// +// _ "github.com/beego/beego/v2/server/web/session/redis_sentinel" +// "github.com/beego/beego/v2/server/web/session" +// // ) // // func init() { @@ -101,7 +103,10 @@ func (rs *SessionStore) SessionID(context.Context) string { // SessionRelease save session values to redis_sentinel func (rs *SessionStore) SessionRelease(ctx context.Context, w http.ResponseWriter) { - b, err := session.EncodeGob(rs.values) + rs.lock.RLock() + values := rs.values + rs.lock.RUnlock() + b, err := session.EncodeGob(values) if err != nil { return } diff --git a/server/web/session/sess_cookie.go b/server/web/session/sess_cookie.go index 622fb2fe..2d6f60fa 100644 --- a/server/web/session/sess_cookie.go +++ b/server/web/session/sess_cookie.go @@ -75,9 +75,11 @@ func (st *CookieSessionStore) SessionID(context.Context) string { // SessionRelease Write cookie session to http response cookie func (st *CookieSessionStore) SessionRelease(ctx context.Context, w http.ResponseWriter) { - st.lock.Lock() - encodedCookie, err := encodeCookie(cookiepder.block, cookiepder.config.SecurityKey, cookiepder.config.SecurityName, st.values) - st.lock.Unlock() + st.lock.RLock() + values := st.values + st.lock.RUnlock() + encodedCookie, err := encodeCookie( + cookiepder.block, cookiepder.config.SecurityKey, cookiepder.config.SecurityName, values) if err == nil { cookie := &http.Cookie{ Name: cookiepder.config.CookieName, @@ -110,11 +112,12 @@ type CookieProvider struct { // SessionInit Init cookie session provider with max lifetime and config json. // maxlifetime is ignored. // json config: -// securityKey - hash string -// blockKey - gob encode hash string. it's saved as aes crypto. -// securityName - recognized name in encoded cookie string -// cookieName - cookie name -// maxage - cookie max life time. +// +// securityKey - hash string +// blockKey - gob encode hash string. it's saved as aes crypto. +// securityName - recognized name in encoded cookie string +// cookieName - cookie name +// maxage - cookie max life time. func (pder *CookieProvider) SessionInit(ctx context.Context, maxlifetime int64, config string) error { pder.config = &cookieConfig{} err := json.Unmarshal([]byte(config), pder.config) diff --git a/server/web/session/ssdb/sess_ssdb.go b/server/web/session/ssdb/sess_ssdb.go index c9add89e..73137b23 100644 --- a/server/web/session/ssdb/sess_ssdb.go +++ b/server/web/session/ssdb/sess_ssdb.go @@ -205,7 +205,10 @@ func (s *SessionStore) SessionID(context.Context) string { // SessionRelease Store the keyvalues into ssdb func (s *SessionStore) SessionRelease(ctx context.Context, w http.ResponseWriter) { - b, err := session.EncodeGob(s.values) + s.lock.RLock() + values := s.values + s.lock.RUnlock() + b, err := session.EncodeGob(values) if err != nil { return }